forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			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.
 | |
|   lldb_utility::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
 |