652 lines
21 KiB
C++
652 lines
21 KiB
C++
//===-- DarwinProcessLauncher.cpp -------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
// DarwinProcessLauncher.cpp
|
|
// lldb
|
|
//
|
|
// Created by Todd Fiala on 8/30/16.
|
|
//
|
|
//
|
|
|
|
#include "DarwinProcessLauncher.h"
|
|
|
|
// C includes
|
|
#include <spawn.h>
|
|
#include <sys/ptrace.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#ifndef _POSIX_SPAWN_DISABLE_ASLR
|
|
#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
|
|
#endif
|
|
|
|
// LLDB includes
|
|
#include "lldb/lldb-enumerations.h"
|
|
|
|
#include "lldb/Host/PseudoTerminal.h"
|
|
#include "lldb/Target/ProcessLaunchInfo.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/Status.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
#include "llvm/Support/Errno.h"
|
|
|
|
#include "CFBundle.h"
|
|
#include "CFString.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::process_darwin;
|
|
using namespace lldb_private::darwin_process_launcher;
|
|
|
|
namespace {
|
|
static LaunchFlavor g_launch_flavor = LaunchFlavor::Default;
|
|
}
|
|
|
|
namespace lldb_private {
|
|
namespace darwin_process_launcher {
|
|
|
|
static uint32_t GetCPUTypeForLocalProcess(::pid_t pid) {
|
|
int mib[CTL_MAXNAME] = {
|
|
0,
|
|
};
|
|
size_t len = CTL_MAXNAME;
|
|
if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
|
|
return 0;
|
|
|
|
mib[len] = pid;
|
|
len++;
|
|
|
|
cpu_type_t cpu;
|
|
size_t cpu_len = sizeof(cpu);
|
|
if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
|
|
cpu = 0;
|
|
return cpu;
|
|
}
|
|
|
|
static bool ResolveExecutablePath(const char *path, char *resolved_path,
|
|
size_t resolved_path_size) {
|
|
if (path == NULL || path[0] == '\0')
|
|
return false;
|
|
|
|
char max_path[PATH_MAX];
|
|
std::string result;
|
|
CFString::GlobPath(path, result);
|
|
|
|
if (result.empty())
|
|
result = path;
|
|
|
|
struct stat path_stat;
|
|
if (::stat(path, &path_stat) == 0) {
|
|
if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
|
|
CFBundle bundle(path);
|
|
CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
|
|
if (url.get()) {
|
|
if (::CFURLGetFileSystemRepresentation(
|
|
url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (realpath(path, max_path)) {
|
|
// Found the path relatively...
|
|
::strncpy(resolved_path, max_path, resolved_path_size);
|
|
return strlen(resolved_path) + 1 < resolved_path_size;
|
|
} else {
|
|
// Not a relative path, check the PATH environment variable if the
|
|
const char *PATH = getenv("PATH");
|
|
if (PATH) {
|
|
const char *curr_path_start = PATH;
|
|
const char *curr_path_end;
|
|
while (curr_path_start && *curr_path_start) {
|
|
curr_path_end = strchr(curr_path_start, ':');
|
|
if (curr_path_end == NULL) {
|
|
result.assign(curr_path_start);
|
|
curr_path_start = NULL;
|
|
} else if (curr_path_end > curr_path_start) {
|
|
size_t len = curr_path_end - curr_path_start;
|
|
result.assign(curr_path_start, len);
|
|
curr_path_start += len + 1;
|
|
} else
|
|
break;
|
|
|
|
result += '/';
|
|
result += path;
|
|
struct stat s;
|
|
if (stat(result.c_str(), &s) == 0) {
|
|
::strncpy(resolved_path, result.c_str(), resolved_path_size);
|
|
return result.size() + 1 < resolved_path_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// TODO check if we have a general purpose fork and exec. We may be
|
|
// able to get rid of this entirely.
|
|
static Status ForkChildForPTraceDebugging(const char *path, char const *argv[],
|
|
char const *envp[], ::pid_t *pid,
|
|
int *pty_fd) {
|
|
Status error;
|
|
if (!path || !argv || !envp || !pid || !pty_fd) {
|
|
error.SetErrorString("invalid arguments");
|
|
return error;
|
|
}
|
|
|
|
// Use a fork that ties the child process's stdin/out/err to a pseudo
|
|
// terminal so we can read it in our MachProcess::STDIOThread
|
|
// as unbuffered io.
|
|
PseudoTerminal pty;
|
|
char fork_error[256];
|
|
memset(fork_error, 0, sizeof(fork_error));
|
|
*pid = static_cast<::pid_t>(pty.Fork(fork_error, sizeof(fork_error)));
|
|
if (*pid < 0) {
|
|
//--------------------------------------------------------------
|
|
// Status during fork.
|
|
//--------------------------------------------------------------
|
|
*pid = static_cast<::pid_t>(LLDB_INVALID_PROCESS_ID);
|
|
error.SetErrorStringWithFormat("%s(): fork failed: %s", __FUNCTION__,
|
|
fork_error);
|
|
return error;
|
|
} else if (pid == 0) {
|
|
//--------------------------------------------------------------
|
|
// Child process
|
|
//--------------------------------------------------------------
|
|
|
|
// Debug this process.
|
|
::ptrace(PT_TRACE_ME, 0, 0, 0);
|
|
|
|
// Get BSD signals as mach exceptions.
|
|
::ptrace(PT_SIGEXC, 0, 0, 0);
|
|
|
|
// If our parent is setgid, lets make sure we don't inherit those
|
|
// extra powers due to nepotism.
|
|
if (::setgid(getgid()) == 0) {
|
|
// Let the child have its own process group. We need to execute
|
|
// this call in both the child and parent to avoid a race
|
|
// condition between the two processes.
|
|
|
|
// Set the child process group to match its pid.
|
|
::setpgid(0, 0);
|
|
|
|
// Sleep a bit to before the exec call.
|
|
::sleep(1);
|
|
|
|
// Turn this process into the given executable.
|
|
::execv(path, (char *const *)argv);
|
|
}
|
|
// Exit with error code. Child process should have taken
|
|
// over in above exec call and if the exec fails it will
|
|
// exit the child process below.
|
|
::exit(127);
|
|
} else {
|
|
//--------------------------------------------------------------
|
|
// Parent process
|
|
//--------------------------------------------------------------
|
|
// Let the child have its own process group. We need to execute
|
|
// this call in both the child and parent to avoid a race condition
|
|
// between the two processes.
|
|
|
|
// Set the child process group to match its pid
|
|
::setpgid(*pid, *pid);
|
|
if (pty_fd) {
|
|
// Release our master pty file descriptor so the pty class doesn't
|
|
// close it and so we can continue to use it in our STDIO thread
|
|
*pty_fd = pty.ReleaseMasterFileDescriptor();
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static Status
|
|
CreatePosixSpawnFileAction(const FileAction &action,
|
|
posix_spawn_file_actions_t *file_actions) {
|
|
Status error;
|
|
|
|
// Log it.
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
if (log) {
|
|
StreamString stream;
|
|
stream.PutCString("converting file action for posix_spawn(): ");
|
|
action.Dump(stream);
|
|
stream.Flush();
|
|
log->PutCString(stream.GetString().c_str());
|
|
}
|
|
|
|
// Validate args.
|
|
if (!file_actions) {
|
|
error.SetErrorString("mandatory file_actions arg is null");
|
|
return error;
|
|
}
|
|
|
|
// Build the posix file action.
|
|
switch (action.GetAction()) {
|
|
case FileAction::eFileActionOpen: {
|
|
const int error_code = ::posix_spawn_file_actions_addopen(
|
|
file_actions, action.GetFD(), action.GetPath(),
|
|
action.GetActionArgument(), 0);
|
|
if (error_code != 0) {
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FileAction::eFileActionClose: {
|
|
const int error_code =
|
|
::posix_spawn_file_actions_addclose(file_actions, action.GetFD());
|
|
if (error_code != 0) {
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FileAction::eFileActionDuplicate: {
|
|
const int error_code = ::posix_spawn_file_actions_adddup2(
|
|
file_actions, action.GetFD(), action.GetActionArgument());
|
|
if (error_code != 0) {
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FileAction::eFileActionNone:
|
|
default:
|
|
if (log)
|
|
log->Printf("%s(): unsupported file action %u", __FUNCTION__,
|
|
action.GetAction());
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static Status PosixSpawnChildForPTraceDebugging(const char *path,
|
|
ProcessLaunchInfo &launch_info,
|
|
::pid_t *pid,
|
|
cpu_type_t *actual_cpu_type) {
|
|
Status error;
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (!pid) {
|
|
error.SetErrorStringWithFormat("%s(): pid arg cannot be null",
|
|
__FUNCTION__);
|
|
return error;
|
|
}
|
|
|
|
posix_spawnattr_t attr;
|
|
short flags;
|
|
if (log) {
|
|
StreamString stream;
|
|
stream.Printf("%s(path='%s',...)\n", __FUNCTION__, path);
|
|
launch_info.Dump(stream, nullptr);
|
|
stream.Flush();
|
|
log->PutCString(stream.GetString().c_str());
|
|
}
|
|
|
|
int error_code;
|
|
if ((error_code = ::posix_spawnattr_init(&attr)) != 0) {
|
|
if (log)
|
|
log->Printf("::posix_spawnattr_init(&attr) failed");
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
|
|
// Ensure we clean up the spawnattr structure however we exit this
|
|
// function.
|
|
std::unique_ptr<posix_spawnattr_t, int (*)(posix_spawnattr_t *)> spawnattr_up(
|
|
&attr, ::posix_spawnattr_destroy);
|
|
|
|
flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF |
|
|
POSIX_SPAWN_SETSIGMASK;
|
|
if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
|
|
flags |= _POSIX_SPAWN_DISABLE_ASLR;
|
|
|
|
sigset_t no_signals;
|
|
sigset_t all_signals;
|
|
sigemptyset(&no_signals);
|
|
sigfillset(&all_signals);
|
|
::posix_spawnattr_setsigmask(&attr, &no_signals);
|
|
::posix_spawnattr_setsigdefault(&attr, &all_signals);
|
|
|
|
if ((error_code = ::posix_spawnattr_setflags(&attr, flags)) != 0) {
|
|
LLDB_LOG(log,
|
|
"::posix_spawnattr_setflags(&attr, "
|
|
"POSIX_SPAWN_START_SUSPENDED{0}) failed: {1}",
|
|
flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR"
|
|
: "",
|
|
llvm::sys::StrError(error_code));
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
|
|
#if !defined(__arm__)
|
|
|
|
// We don't need to do this for ARM, and we really shouldn't now that we
|
|
// have multiple CPU subtypes and no posix_spawnattr call that allows us
|
|
// to set which CPU subtype to launch...
|
|
cpu_type_t desired_cpu_type = launch_info.GetArchitecture().GetMachOCPUType();
|
|
if (desired_cpu_type != LLDB_INVALID_CPUTYPE) {
|
|
size_t ocount = 0;
|
|
error_code =
|
|
::posix_spawnattr_setbinpref_np(&attr, 1, &desired_cpu_type, &ocount);
|
|
if (error_code != 0) {
|
|
LLDB_LOG(log,
|
|
"::posix_spawnattr_setbinpref_np(&attr, 1, "
|
|
"cpu_type = {0:x8}, count => {1}): {2}",
|
|
desired_cpu_type, ocount, llvm::sys::StrError(error_code));
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
if (ocount != 1) {
|
|
error.SetErrorStringWithFormat("posix_spawnattr_setbinpref_np "
|
|
"did not set the expected number "
|
|
"of cpu_type entries: expected 1 "
|
|
"but was %zu",
|
|
ocount);
|
|
return error;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
posix_spawn_file_actions_t file_actions;
|
|
if ((error_code = ::posix_spawn_file_actions_init(&file_actions)) != 0) {
|
|
LLDB_LOG(log, "::posix_spawn_file_actions_init(&file_actions) failed: {0}",
|
|
llvm::sys::StrError(error_code));
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
|
|
// Ensure we clean up file actions however we exit this. When the
|
|
// file_actions_up below goes out of scope, we'll get our file action
|
|
// cleanup.
|
|
std::unique_ptr<posix_spawn_file_actions_t,
|
|
int (*)(posix_spawn_file_actions_t *)>
|
|
file_actions_up(&file_actions, ::posix_spawn_file_actions_destroy);
|
|
|
|
// We assume the caller has setup the file actions appropriately. We
|
|
// are not in the business of figuring out what we really need here.
|
|
// lldb-server will have already called FinalizeFileActions() as well
|
|
// to button these up properly.
|
|
const size_t num_actions = launch_info.GetNumFileActions();
|
|
for (size_t action_index = 0; action_index < num_actions; ++action_index) {
|
|
const FileAction *const action =
|
|
launch_info.GetFileActionAtIndex(action_index);
|
|
if (!action)
|
|
continue;
|
|
|
|
error = CreatePosixSpawnFileAction(*action, &file_actions);
|
|
if (!error.Success()) {
|
|
if (log)
|
|
log->Printf("%s(): error converting FileAction to posix_spawn "
|
|
"file action: %s",
|
|
__FUNCTION__, error.AsCString());
|
|
return error;
|
|
}
|
|
}
|
|
|
|
// TODO: Verify if we can set the working directory back immediately
|
|
// after the posix_spawnp call without creating a race condition???
|
|
const char *const working_directory =
|
|
launch_info.GetWorkingDirectory().GetCString();
|
|
if (working_directory && working_directory[0])
|
|
::chdir(working_directory);
|
|
|
|
auto argv = launch_info.GetArguments().GetArgumentVector();
|
|
auto envp = launch_info.GetEnvironmentEntries().GetArgumentVector();
|
|
error_code = ::posix_spawnp(pid, path, &file_actions, &attr,
|
|
(char *const *)argv, (char *const *)envp);
|
|
if (error_code != 0) {
|
|
LLDB_LOG(log,
|
|
"::posix_spawnp(pid => {0}, path = '{1}', file_actions "
|
|
"= {2}, attr = {3}, argv = {4}, envp = {5}) failed: {6}",
|
|
pid, path, &file_actions, &attr, argv, envp,
|
|
llvm::sys::StrError(error_code));
|
|
error.SetError(error_code, eErrorTypePOSIX);
|
|
return error;
|
|
}
|
|
|
|
// Validate we got a pid.
|
|
if (pid == LLDB_INVALID_PROCESS_ID) {
|
|
error.SetErrorString("posix_spawn() did not indicate a failure but it "
|
|
"failed to return a pid, aborting.");
|
|
return error;
|
|
}
|
|
|
|
if (actual_cpu_type) {
|
|
*actual_cpu_type = GetCPUTypeForLocalProcess(*pid);
|
|
if (log)
|
|
log->Printf("%s(): cpu type for launched process pid=%i: "
|
|
"cpu_type=0x%8.8x",
|
|
__FUNCTION__, *pid, *actual_cpu_type);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
Status LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
|
|
LaunchFlavor *launch_flavor) {
|
|
Status error;
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (!launch_flavor) {
|
|
error.SetErrorString("mandatory launch_flavor field was null");
|
|
return error;
|
|
}
|
|
|
|
if (log) {
|
|
StreamString stream;
|
|
stream.Printf("NativeProcessDarwin::%s(): launching with the "
|
|
"following launch info:",
|
|
__FUNCTION__);
|
|
launch_info.Dump(stream, nullptr);
|
|
stream.Flush();
|
|
log->PutCString(stream.GetString().c_str());
|
|
}
|
|
|
|
// Retrieve the binary name given to us.
|
|
char given_path[PATH_MAX];
|
|
given_path[0] = '\0';
|
|
launch_info.GetExecutableFile().GetPath(given_path, sizeof(given_path));
|
|
|
|
// Determine the manner in which we'll launch.
|
|
*launch_flavor = g_launch_flavor;
|
|
if (*launch_flavor == LaunchFlavor::Default) {
|
|
// Our default launch method is posix spawn
|
|
*launch_flavor = LaunchFlavor::PosixSpawn;
|
|
#if defined WITH_FBS
|
|
// Check if we have an app bundle, if so launch using BackBoard Services.
|
|
if (strstr(given_path, ".app")) {
|
|
*launch_flavor = eLaunchFlavorFBS;
|
|
}
|
|
#elif defined WITH_BKS
|
|
// Check if we have an app bundle, if so launch using BackBoard Services.
|
|
if (strstr(given_path, ".app")) {
|
|
*launch_flavor = eLaunchFlavorBKS;
|
|
}
|
|
#elif defined WITH_SPRINGBOARD
|
|
// Check if we have an app bundle, if so launch using SpringBoard.
|
|
if (strstr(given_path, ".app")) {
|
|
*launch_flavor = eLaunchFlavorSpringBoard;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Attempt to resolve the binary name to an absolute path.
|
|
char resolved_path[PATH_MAX];
|
|
resolved_path[0] = '\0';
|
|
|
|
if (log)
|
|
log->Printf("%s(): attempting to resolve given binary path: \"%s\"",
|
|
__FUNCTION__, given_path);
|
|
|
|
// If we fail to resolve the path to our executable, then just use what we
|
|
// were given and hope for the best
|
|
if (!ResolveExecutablePath(given_path, resolved_path,
|
|
sizeof(resolved_path))) {
|
|
if (log)
|
|
log->Printf("%s(): failed to resolve binary path, using "
|
|
"what was given verbatim and hoping for the best",
|
|
__FUNCTION__);
|
|
::strncpy(resolved_path, given_path, sizeof(resolved_path));
|
|
} else {
|
|
if (log)
|
|
log->Printf("%s(): resolved given binary path to: \"%s\"", __FUNCTION__,
|
|
resolved_path);
|
|
}
|
|
|
|
char launch_err_str[PATH_MAX];
|
|
launch_err_str[0] = '\0';
|
|
|
|
// TODO figure out how to handle QSetProcessEvent
|
|
// const char *process_event = ctx.GetProcessEvent();
|
|
|
|
// Ensure the binary is there.
|
|
struct stat path_stat;
|
|
if (::stat(resolved_path, &path_stat) == -1) {
|
|
error.SetErrorToErrno();
|
|
return error;
|
|
}
|
|
|
|
// Fork a child process for debugging
|
|
// state_callback(eStateLaunching);
|
|
|
|
const auto argv = launch_info.GetArguments().GetConstArgumentVector();
|
|
const auto envp =
|
|
launch_info.GetEnvironmentEntries().GetConstArgumentVector();
|
|
|
|
switch (*launch_flavor) {
|
|
case LaunchFlavor::ForkExec: {
|
|
::pid_t pid = LLDB_INVALID_PROCESS_ID;
|
|
error = ForkChildForPTraceDebugging(resolved_path, argv, envp, &pid,
|
|
pty_master_fd);
|
|
if (error.Success()) {
|
|
launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
|
|
} else {
|
|
// Reset any variables that might have been set during a failed
|
|
// launch attempt.
|
|
if (pty_master_fd)
|
|
*pty_master_fd = -1;
|
|
|
|
// We're done.
|
|
return error;
|
|
}
|
|
} break;
|
|
|
|
#ifdef WITH_FBS
|
|
case LaunchFlavor::FBS: {
|
|
const char *app_ext = strstr(path, ".app");
|
|
if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
|
|
std::string app_bundle_path(path, app_ext + strlen(".app"));
|
|
m_flags |= eMachProcessFlagsUsingFBS;
|
|
if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
|
|
no_stdio, disable_aslr, event_data,
|
|
launch_err) != 0)
|
|
return m_pid; // A successful SBLaunchForDebug() returns and assigns a
|
|
// non-zero m_pid.
|
|
else
|
|
break; // We tried a FBS launch, but didn't succeed lets get out
|
|
}
|
|
} break;
|
|
#endif
|
|
|
|
#ifdef WITH_BKS
|
|
case LaunchFlavor::BKS: {
|
|
const char *app_ext = strstr(path, ".app");
|
|
if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
|
|
std::string app_bundle_path(path, app_ext + strlen(".app"));
|
|
m_flags |= eMachProcessFlagsUsingBKS;
|
|
if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
|
|
no_stdio, disable_aslr, event_data,
|
|
launch_err) != 0)
|
|
return m_pid; // A successful SBLaunchForDebug() returns and assigns a
|
|
// non-zero m_pid.
|
|
else
|
|
break; // We tried a BKS launch, but didn't succeed lets get out
|
|
}
|
|
} break;
|
|
#endif
|
|
|
|
#ifdef WITH_SPRINGBOARD
|
|
case LaunchFlavor::SpringBoard: {
|
|
// .../whatever.app/whatever ?
|
|
// Or .../com.apple.whatever.app/whatever -- be careful of ".app" in
|
|
// "com.apple.whatever" here
|
|
const char *app_ext = strstr(path, ".app/");
|
|
if (app_ext == NULL) {
|
|
// .../whatever.app ?
|
|
int len = strlen(path);
|
|
if (len > 5) {
|
|
if (strcmp(path + len - 4, ".app") == 0) {
|
|
app_ext = path + len - 4;
|
|
}
|
|
}
|
|
}
|
|
if (app_ext) {
|
|
std::string app_bundle_path(path, app_ext + strlen(".app"));
|
|
if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio,
|
|
disable_aslr, launch_err) != 0)
|
|
return m_pid; // A successful SBLaunchForDebug() returns and assigns a
|
|
// non-zero m_pid.
|
|
else
|
|
break; // We tried a springboard launch, but didn't succeed lets get out
|
|
}
|
|
} break;
|
|
#endif
|
|
|
|
case LaunchFlavor::PosixSpawn: {
|
|
::pid_t pid = LLDB_INVALID_PROCESS_ID;
|
|
|
|
// Retrieve paths for stdin/stdout/stderr.
|
|
cpu_type_t actual_cpu_type = 0;
|
|
error = PosixSpawnChildForPTraceDebugging(resolved_path, launch_info, &pid,
|
|
&actual_cpu_type);
|
|
if (error.Success()) {
|
|
launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
|
|
if (pty_master_fd)
|
|
*pty_master_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
|
|
} else {
|
|
// Reset any variables that might have been set during a failed
|
|
// launch attempt.
|
|
if (pty_master_fd)
|
|
*pty_master_fd = -1;
|
|
|
|
// We're done.
|
|
return error;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// Invalid launch flavor.
|
|
error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): unknown "
|
|
"launch flavor %d",
|
|
__FUNCTION__, (int)*launch_flavor);
|
|
return error;
|
|
}
|
|
|
|
if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) {
|
|
// If we don't have a valid process ID and no one has set the error,
|
|
// then return a generic error.
|
|
if (error.Success())
|
|
error.SetErrorStringWithFormat("%s(): failed to launch, no reason "
|
|
"specified",
|
|
__FUNCTION__);
|
|
}
|
|
|
|
// We're done with the launch side of the operation.
|
|
return error;
|
|
}
|
|
}
|
|
} // namespaces
|