1 //===-- DarwinProcessLauncher.cpp -------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 // DarwinProcessLauncher.cpp
13 // Created by Todd Fiala on 8/30/16.
17 #include "DarwinProcessLauncher.h"
21 #include <sys/ptrace.h>
23 #include <sys/sysctl.h>
25 #ifndef _POSIX_SPAWN_DISABLE_ASLR
26 #define _POSIX_SPAWN_DISABLE_ASLR 0x0100
30 #include "lldb/lldb-enumerations.h"
32 #include "lldb/Host/PseudoTerminal.h"
33 #include "lldb/Target/ProcessLaunchInfo.h"
34 #include "lldb/Utility/Log.h"
35 #include "lldb/Utility/Status.h"
36 #include "lldb/Utility/StreamString.h"
37 #include "llvm/Support/Errno.h"
43 using namespace lldb_private;
44 using namespace lldb_private::process_darwin;
45 using namespace lldb_private::darwin_process_launcher;
48 static LaunchFlavor g_launch_flavor = LaunchFlavor::Default;
51 namespace lldb_private {
52 namespace darwin_process_launcher {
54 static uint32_t GetCPUTypeForLocalProcess(::pid_t pid) {
55 int mib[CTL_MAXNAME] = {
58 size_t len = CTL_MAXNAME;
59 if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
66 size_t cpu_len = sizeof(cpu);
67 if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
72 static bool ResolveExecutablePath(const char *path, char *resolved_path,
73 size_t resolved_path_size) {
74 if (path == NULL || path[0] == '\0')
77 char max_path[PATH_MAX];
79 CFString::GlobPath(path, result);
84 struct stat path_stat;
85 if (::stat(path, &path_stat) == 0) {
86 if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
87 CFBundle bundle(path);
88 CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
90 if (::CFURLGetFileSystemRepresentation(
91 url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
97 if (realpath(path, max_path)) {
98 // Found the path relatively...
99 ::strncpy(resolved_path, max_path, resolved_path_size);
100 return strlen(resolved_path) + 1 < resolved_path_size;
102 // Not a relative path, check the PATH environment variable if the
103 const char *PATH = getenv("PATH");
105 const char *curr_path_start = PATH;
106 const char *curr_path_end;
107 while (curr_path_start && *curr_path_start) {
108 curr_path_end = strchr(curr_path_start, ':');
109 if (curr_path_end == NULL) {
110 result.assign(curr_path_start);
111 curr_path_start = NULL;
112 } else if (curr_path_end > curr_path_start) {
113 size_t len = curr_path_end - curr_path_start;
114 result.assign(curr_path_start, len);
115 curr_path_start += len + 1;
122 if (stat(result.c_str(), &s) == 0) {
123 ::strncpy(resolved_path, result.c_str(), resolved_path_size);
124 return result.size() + 1 < resolved_path_size;
132 // TODO check if we have a general purpose fork and exec. We may be
133 // able to get rid of this entirely.
134 static Status ForkChildForPTraceDebugging(const char *path, char const *argv[],
135 char const *envp[], ::pid_t *pid,
138 if (!path || !argv || !envp || !pid || !pty_fd) {
139 error.SetErrorString("invalid arguments");
143 // Use a fork that ties the child process's stdin/out/err to a pseudo
144 // terminal so we can read it in our MachProcess::STDIOThread as unbuffered
147 char fork_error[256];
148 memset(fork_error, 0, sizeof(fork_error));
149 *pid = static_cast<::pid_t>(pty.Fork(fork_error, sizeof(fork_error)));
151 // Status during fork.
152 *pid = static_cast<::pid_t>(LLDB_INVALID_PROCESS_ID);
153 error.SetErrorStringWithFormat("%s(): fork failed: %s", __FUNCTION__,
156 } else if (pid == 0) {
159 // Debug this process.
160 ::ptrace(PT_TRACE_ME, 0, 0, 0);
162 // Get BSD signals as mach exceptions.
163 ::ptrace(PT_SIGEXC, 0, 0, 0);
165 // If our parent is setgid, lets make sure we don't inherit those extra
166 // powers due to nepotism.
167 if (::setgid(getgid()) == 0) {
168 // Let the child have its own process group. We need to execute this call
169 // in both the child and parent to avoid a race condition between the two
172 // Set the child process group to match its pid.
175 // Sleep a bit to before the exec call.
178 // Turn this process into the given executable.
179 ::execv(path, (char *const *)argv);
181 // Exit with error code. Child process should have taken over in above exec
182 // call and if the exec fails it will exit the child process below.
186 // Let the child have its own process group. We need to execute this call
187 // in both the child and parent to avoid a race condition between the two
190 // Set the child process group to match its pid
191 ::setpgid(*pid, *pid);
193 // Release our master pty file descriptor so the pty class doesn't close
194 // it and so we can continue to use it in our STDIO thread
195 *pty_fd = pty.ReleaseMasterFileDescriptor();
202 CreatePosixSpawnFileAction(const FileAction &action,
203 posix_spawn_file_actions_t *file_actions) {
207 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
210 stream.PutCString("converting file action for posix_spawn(): ");
213 log->PutCString(stream.GetString().c_str());
218 error.SetErrorString("mandatory file_actions arg is null");
222 // Build the posix file action.
223 switch (action.GetAction()) {
224 case FileAction::eFileActionOpen: {
225 const int error_code = ::posix_spawn_file_actions_addopen(
226 file_actions, action.GetFD(), action.GetPath(),
227 action.GetActionArgument(), 0);
228 if (error_code != 0) {
229 error.SetError(error_code, eErrorTypePOSIX);
235 case FileAction::eFileActionClose: {
236 const int error_code =
237 ::posix_spawn_file_actions_addclose(file_actions, action.GetFD());
238 if (error_code != 0) {
239 error.SetError(error_code, eErrorTypePOSIX);
245 case FileAction::eFileActionDuplicate: {
246 const int error_code = ::posix_spawn_file_actions_adddup2(
247 file_actions, action.GetFD(), action.GetActionArgument());
248 if (error_code != 0) {
249 error.SetError(error_code, eErrorTypePOSIX);
255 case FileAction::eFileActionNone:
258 log->Printf("%s(): unsupported file action %u", __FUNCTION__,
266 static Status PosixSpawnChildForPTraceDebugging(const char *path,
267 ProcessLaunchInfo &launch_info,
269 cpu_type_t *actual_cpu_type) {
271 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
274 error.SetErrorStringWithFormat("%s(): pid arg cannot be null",
279 posix_spawnattr_t attr;
283 stream.Printf("%s(path='%s',...)\n", __FUNCTION__, path);
284 launch_info.Dump(stream, nullptr);
286 log->PutCString(stream.GetString().c_str());
290 if ((error_code = ::posix_spawnattr_init(&attr)) != 0) {
292 log->Printf("::posix_spawnattr_init(&attr) failed");
293 error.SetError(error_code, eErrorTypePOSIX);
297 // Ensure we clean up the spawnattr structure however we exit this function.
298 std::unique_ptr<posix_spawnattr_t, int (*)(posix_spawnattr_t *)> spawnattr_up(
299 &attr, ::posix_spawnattr_destroy);
301 flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF |
302 POSIX_SPAWN_SETSIGMASK;
303 if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
304 flags |= _POSIX_SPAWN_DISABLE_ASLR;
307 sigset_t all_signals;
308 sigemptyset(&no_signals);
309 sigfillset(&all_signals);
310 ::posix_spawnattr_setsigmask(&attr, &no_signals);
311 ::posix_spawnattr_setsigdefault(&attr, &all_signals);
313 if ((error_code = ::posix_spawnattr_setflags(&attr, flags)) != 0) {
315 "::posix_spawnattr_setflags(&attr, "
316 "POSIX_SPAWN_START_SUSPENDED{0}) failed: {1}",
317 flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR"
319 llvm::sys::StrError(error_code));
320 error.SetError(error_code, eErrorTypePOSIX);
324 #if !defined(__arm__)
326 // We don't need to do this for ARM, and we really shouldn't now that we have
327 // multiple CPU subtypes and no posix_spawnattr call that allows us to set
328 // which CPU subtype to launch...
329 cpu_type_t desired_cpu_type = launch_info.GetArchitecture().GetMachOCPUType();
330 if (desired_cpu_type != LLDB_INVALID_CPUTYPE) {
333 ::posix_spawnattr_setbinpref_np(&attr, 1, &desired_cpu_type, &ocount);
334 if (error_code != 0) {
336 "::posix_spawnattr_setbinpref_np(&attr, 1, "
337 "cpu_type = {0:x8}, count => {1}): {2}",
338 desired_cpu_type, ocount, llvm::sys::StrError(error_code));
339 error.SetError(error_code, eErrorTypePOSIX);
343 error.SetErrorStringWithFormat("posix_spawnattr_setbinpref_np "
344 "did not set the expected number "
345 "of cpu_type entries: expected 1 "
353 posix_spawn_file_actions_t file_actions;
354 if ((error_code = ::posix_spawn_file_actions_init(&file_actions)) != 0) {
355 LLDB_LOG(log, "::posix_spawn_file_actions_init(&file_actions) failed: {0}",
356 llvm::sys::StrError(error_code));
357 error.SetError(error_code, eErrorTypePOSIX);
361 // Ensure we clean up file actions however we exit this. When the
362 // file_actions_up below goes out of scope, we'll get our file action
364 std::unique_ptr<posix_spawn_file_actions_t,
365 int (*)(posix_spawn_file_actions_t *)>
366 file_actions_up(&file_actions, ::posix_spawn_file_actions_destroy);
368 // We assume the caller has setup the file actions appropriately. We are not
369 // in the business of figuring out what we really need here. lldb-server will
370 // have already called FinalizeFileActions() as well to button these up
372 const size_t num_actions = launch_info.GetNumFileActions();
373 for (size_t action_index = 0; action_index < num_actions; ++action_index) {
374 const FileAction *const action =
375 launch_info.GetFileActionAtIndex(action_index);
379 error = CreatePosixSpawnFileAction(*action, &file_actions);
380 if (!error.Success()) {
382 log->Printf("%s(): error converting FileAction to posix_spawn "
384 __FUNCTION__, error.AsCString());
389 // TODO: Verify if we can set the working directory back immediately
390 // after the posix_spawnp call without creating a race condition???
391 const char *const working_directory =
392 launch_info.GetWorkingDirectory().GetCString();
393 if (working_directory && working_directory[0])
394 ::chdir(working_directory);
396 auto argv = launch_info.GetArguments().GetArgumentVector();
397 auto envp = launch_info.GetEnvironmentEntries().GetArgumentVector();
398 error_code = ::posix_spawnp(pid, path, &file_actions, &attr,
399 (char *const *)argv, (char *const *)envp);
400 if (error_code != 0) {
402 "::posix_spawnp(pid => {0}, path = '{1}', file_actions "
403 "= {2}, attr = {3}, argv = {4}, envp = {5}) failed: {6}",
404 pid, path, &file_actions, &attr, argv, envp,
405 llvm::sys::StrError(error_code));
406 error.SetError(error_code, eErrorTypePOSIX);
410 // Validate we got a pid.
411 if (pid == LLDB_INVALID_PROCESS_ID) {
412 error.SetErrorString("posix_spawn() did not indicate a failure but it "
413 "failed to return a pid, aborting.");
417 if (actual_cpu_type) {
418 *actual_cpu_type = GetCPUTypeForLocalProcess(*pid);
420 log->Printf("%s(): cpu type for launched process pid=%i: "
422 __FUNCTION__, *pid, *actual_cpu_type);
428 Status LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
429 LaunchFlavor *launch_flavor) {
431 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
433 if (!launch_flavor) {
434 error.SetErrorString("mandatory launch_flavor field was null");
440 stream.Printf("NativeProcessDarwin::%s(): launching with the "
441 "following launch info:",
443 launch_info.Dump(stream, nullptr);
445 log->PutCString(stream.GetString().c_str());
448 // Retrieve the binary name given to us.
449 char given_path[PATH_MAX];
450 given_path[0] = '\0';
451 launch_info.GetExecutableFile().GetPath(given_path, sizeof(given_path));
453 // Determine the manner in which we'll launch.
454 *launch_flavor = g_launch_flavor;
455 if (*launch_flavor == LaunchFlavor::Default) {
456 // Our default launch method is posix spawn
457 *launch_flavor = LaunchFlavor::PosixSpawn;
459 // Check if we have an app bundle, if so launch using BackBoard Services.
460 if (strstr(given_path, ".app")) {
461 *launch_flavor = eLaunchFlavorFBS;
463 #elif defined WITH_BKS
464 // Check if we have an app bundle, if so launch using BackBoard Services.
465 if (strstr(given_path, ".app")) {
466 *launch_flavor = eLaunchFlavorBKS;
468 #elif defined WITH_SPRINGBOARD
469 // Check if we have an app bundle, if so launch using SpringBoard.
470 if (strstr(given_path, ".app")) {
471 *launch_flavor = eLaunchFlavorSpringBoard;
476 // Attempt to resolve the binary name to an absolute path.
477 char resolved_path[PATH_MAX];
478 resolved_path[0] = '\0';
481 log->Printf("%s(): attempting to resolve given binary path: \"%s\"",
482 __FUNCTION__, given_path);
484 // If we fail to resolve the path to our executable, then just use what we
485 // were given and hope for the best
486 if (!ResolveExecutablePath(given_path, resolved_path,
487 sizeof(resolved_path))) {
489 log->Printf("%s(): failed to resolve binary path, using "
490 "what was given verbatim and hoping for the best",
492 ::strncpy(resolved_path, given_path, sizeof(resolved_path));
495 log->Printf("%s(): resolved given binary path to: \"%s\"", __FUNCTION__,
499 char launch_err_str[PATH_MAX];
500 launch_err_str[0] = '\0';
502 // TODO figure out how to handle QSetProcessEvent
503 // const char *process_event = ctx.GetProcessEvent();
505 // Ensure the binary is there.
506 struct stat path_stat;
507 if (::stat(resolved_path, &path_stat) == -1) {
508 error.SetErrorToErrno();
512 // Fork a child process for debugging
513 // state_callback(eStateLaunching);
515 const auto argv = launch_info.GetArguments().GetConstArgumentVector();
517 launch_info.GetEnvironmentEntries().GetConstArgumentVector();
519 switch (*launch_flavor) {
520 case LaunchFlavor::ForkExec: {
521 ::pid_t pid = LLDB_INVALID_PROCESS_ID;
522 error = ForkChildForPTraceDebugging(resolved_path, argv, envp, &pid,
524 if (error.Success()) {
525 launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
527 // Reset any variables that might have been set during a failed launch
538 case LaunchFlavor::FBS: {
539 const char *app_ext = strstr(path, ".app");
540 if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
541 std::string app_bundle_path(path, app_ext + strlen(".app"));
542 m_flags |= eMachProcessFlagsUsingFBS;
543 if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
544 no_stdio, disable_aslr, event_data,
546 return m_pid; // A successful SBLaunchForDebug() returns and assigns a
549 break; // We tried a FBS launch, but didn't succeed lets get out
555 case LaunchFlavor::BKS: {
556 const char *app_ext = strstr(path, ".app");
557 if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
558 std::string app_bundle_path(path, app_ext + strlen(".app"));
559 m_flags |= eMachProcessFlagsUsingBKS;
560 if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
561 no_stdio, disable_aslr, event_data,
563 return m_pid; // A successful SBLaunchForDebug() returns and assigns a
566 break; // We tried a BKS launch, but didn't succeed lets get out
571 #ifdef WITH_SPRINGBOARD
572 case LaunchFlavor::SpringBoard: {
573 // .../whatever.app/whatever ?
574 // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in
575 // "com.apple.whatever" here
576 const char *app_ext = strstr(path, ".app/");
577 if (app_ext == NULL) {
578 // .../whatever.app ?
579 int len = strlen(path);
581 if (strcmp(path + len - 4, ".app") == 0) {
582 app_ext = path + len - 4;
587 std::string app_bundle_path(path, app_ext + strlen(".app"));
588 if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio,
589 disable_aslr, launch_err) != 0)
590 return m_pid; // A successful SBLaunchForDebug() returns and assigns a
593 break; // We tried a springboard launch, but didn't succeed lets get out
598 case LaunchFlavor::PosixSpawn: {
599 ::pid_t pid = LLDB_INVALID_PROCESS_ID;
601 // Retrieve paths for stdin/stdout/stderr.
602 cpu_type_t actual_cpu_type = 0;
603 error = PosixSpawnChildForPTraceDebugging(resolved_path, launch_info, &pid,
605 if (error.Success()) {
606 launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
608 *pty_master_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
610 // Reset any variables that might have been set during a failed launch
622 // Invalid launch flavor.
623 error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): unknown "
625 __FUNCTION__, (int)*launch_flavor);
629 if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) {
630 // If we don't have a valid process ID and no one has set the error, then
631 // return a generic error.
633 error.SetErrorStringWithFormat("%s(): failed to launch, no reason "
638 // We're done with the launch side of the operation.