LLDB mainline
Host.mm
Go to the documentation of this file.
1//===-- Host.mm -------------------------------------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Host/Host.h"
11
12#include <AvailabilityMacros.h>
13#include <TargetConditionals.h>
14
15#if TARGET_OS_OSX
16#define __XPC_PRIVATE_H__
17#include <xpc/xpc.h>
18
19#define LaunchUsingXPCRightName "com.apple.lldb.RootDebuggingXPCService"
20
21// These XPC messaging keys are used for communication between Host.mm and the
22// XPC service.
23#define LauncherXPCServiceAuthKey "auth-key"
24#define LauncherXPCServiceArgPrefxKey "arg"
25#define LauncherXPCServiceEnvPrefxKey "env"
26#define LauncherXPCServiceCPUTypeKey "cpuType"
27#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags"
28#define LauncherXPCServiceStdInPathKeyKey "stdInPath"
29#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath"
30#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath"
31#define LauncherXPCServiceChildPIDKey "childPID"
32#define LauncherXPCServiceErrorTypeKey "errorType"
33#define LauncherXPCServiceCodeTypeKey "errorCode"
34
35#include <bsm/audit.h>
36#include <bsm/audit_session.h>
37#endif
38
39#include "llvm/TargetParser/Host.h"
40
41#include <asl.h>
42#include <crt_externs.h>
43#include <cstdio>
44#include <cstdlib>
45#include <dlfcn.h>
46#include <grp.h>
47#include <libproc.h>
48#include <pwd.h>
49#include <spawn.h>
50#include <sys/proc.h>
51#include <sys/stat.h>
52#include <sys/sysctl.h>
53#include <sys/types.h>
54#include <unistd.h>
55
58#include "lldb/Host/HostInfo.h"
65#include "lldb/Utility/Endian.h"
67#include "lldb/Utility/Log.h"
72#include "lldb/lldb-defines.h"
73
74#include "llvm/ADT/ScopeExit.h"
75#include "llvm/Support/Errno.h"
76#include "llvm/Support/FileSystem.h"
77
78#include "../cfcpp/CFCBundle.h"
79#include "../cfcpp/CFCMutableArray.h"
80#include "../cfcpp/CFCMutableDictionary.h"
81#include "../cfcpp/CFCReleaser.h"
82#include "../cfcpp/CFCString.h"
83
84#include <objc/objc-auto.h>
85#include <os/log.h>
86
87#include <CoreFoundation/CoreFoundation.h>
88#include <Foundation/Foundation.h>
89
90#ifndef _POSIX_SPAWN_DISABLE_ASLR
91#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
92#endif
93
94extern "C" {
95int __pthread_chdir(const char *path);
96int __pthread_fchdir(int fildes);
97}
98
99using namespace lldb;
100using namespace lldb_private;
101
102static os_log_t g_os_log;
103static std::once_flag g_os_log_once;
104
105void Host::SystemLog(llvm::StringRef message) {
106 if (__builtin_available(macos 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
107 std::call_once(g_os_log_once, []() {
108 g_os_log = os_log_create("com.apple.dt.lldb", "lldb");
109 });
110 os_log(g_os_log, "%{public}s", message.str().c_str());
111 } else {
112 llvm::errs() << message;
113 }
114}
115
116bool Host::GetBundleDirectory(const FileSpec &file,
117 FileSpec &bundle_directory) {
118#if defined(__APPLE__)
119 if (FileSystem::Instance().IsDirectory(file)) {
120 char path[PATH_MAX];
121 if (file.GetPath(path, sizeof(path))) {
122 CFCBundle bundle(path);
123 if (bundle.GetPath(path, sizeof(path))) {
124 bundle_directory.SetFile(path, FileSpec::Style::native);
125 return true;
126 }
127 }
128 }
129#endif
130 bundle_directory.Clear();
131 return false;
132}
133
135#if defined(__APPLE__)
136 if (FileSystem::Instance().IsDirectory(file)) {
137 char path[PATH_MAX];
138 if (file.GetPath(path, sizeof(path))) {
139 CFCBundle bundle(path);
140 CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL());
141 if (url.get()) {
142 if (::CFURLGetFileSystemRepresentation(url.get(), YES, (UInt8 *)path,
143 sizeof(path))) {
144 file.SetFile(path, FileSpec::Style::native);
145 return true;
146 }
147 }
148 }
149 }
150#endif
151 return false;
152}
153
154#if TARGET_OS_OSX
155
156static void *AcceptPIDFromInferior(const char *connect_url) {
157 ConnectionFileDescriptor file_conn;
159 if (file_conn.Connect(connect_url, &error) == eConnectionStatusSuccess) {
160 char pid_str[256];
161 ::memset(pid_str, 0, sizeof(pid_str));
162 ConnectionStatus status;
163 const size_t pid_str_len = file_conn.Read(
164 pid_str, sizeof(pid_str), std::chrono::seconds(0), status, NULL);
165 if (pid_str_len > 0) {
166 int pid = atoi(pid_str);
167 return (void *)(intptr_t)pid;
168 }
169 }
170 return NULL;
171}
172
173const char *applscript_in_new_tty = "tell application \"Terminal\"\n"
174 " activate\n"
175 " do script \"/bin/bash -c '%s';exit\"\n"
176 "end tell\n";
177
178const char *applscript_in_existing_tty = "\
179set the_shell_script to \"/bin/bash -c '%s';exit\"\n\
180tell application \"Terminal\"\n\
181 repeat with the_window in (get windows)\n\
182 repeat with the_tab in tabs of the_window\n\
183 set the_tty to tty in the_tab\n\
184 if the_tty contains \"%s\" then\n\
185 if the_tab is not busy then\n\
186 set selected of the_tab to true\n\
187 set frontmost of the_window to true\n\
188 do script the_shell_script in the_tab\n\
189 return\n\
190 end if\n\
191 end if\n\
192 end repeat\n\
193 end repeat\n\
194 do script the_shell_script\n\
195end tell\n";
196
197static Status
198LaunchInNewTerminalWithAppleScript(const char *exe_path,
199 ProcessLaunchInfo &launch_info) {
201 char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX";
202 if (::mktemp(unix_socket_name) == NULL) {
203 error.SetErrorString("failed to make temporary path for a unix socket");
204 return error;
205 }
206
207 StreamString command;
208 FileSpec darwin_debug_file_spec = HostInfo::GetSupportExeDir();
209 if (!darwin_debug_file_spec) {
210 error.SetErrorString("can't locate the 'darwin-debug' executable");
211 return error;
212 }
213
214 darwin_debug_file_spec.SetFilename("darwin-debug");
215
216 if (!FileSystem::Instance().Exists(darwin_debug_file_spec)) {
217 error.SetErrorStringWithFormat(
218 "the 'darwin-debug' executable doesn't exists at '%s'",
219 darwin_debug_file_spec.GetPath().c_str());
220 return error;
221 }
222
223 char launcher_path[PATH_MAX];
224 darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
225
226 const ArchSpec &arch_spec = launch_info.GetArchitecture();
227 // Only set the architecture if it is valid and if it isn't Haswell (x86_64h).
228 if (arch_spec.IsValid() &&
230 command.Printf("arch -arch %s ", arch_spec.GetArchitectureName());
231
232 command.Printf(R"(\"%s\" --unix-socket=%s)", launcher_path, unix_socket_name);
233
234 if (arch_spec.IsValid())
235 command.Printf(" --arch=%s", arch_spec.GetArchitectureName());
236
237 FileSpec working_dir{launch_info.GetWorkingDirectory()};
238 if (working_dir)
239 command.Printf(R"( --working-dir \"%s\")", working_dir.GetPath().c_str());
240 else {
241 char cwd[PATH_MAX];
242 if (getcwd(cwd, PATH_MAX))
243 command.Printf(R"( --working-dir \"%s\")", cwd);
244 }
245
246 if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
247 command.PutCString(" --disable-aslr");
248
249 // We are launching on this host in a terminal. So compare the environment on
250 // the host to what is supplied in the launch_info. Any items that aren't in
251 // the host environment need to be sent to darwin-debug. If we send all
252 // environment entries, we might blow the max command line length, so we only
253 // send user modified entries.
255
256 for (const auto &KV : launch_info.GetEnvironment()) {
257 auto host_entry = host_env.find(KV.first());
258 if (host_entry == host_env.end() || host_entry->second != KV.second)
259 command.Format(R"( --env=\"{0}\")", Environment::compose(KV));
260 }
261
262 command.PutCString(" -- ");
263
264 const char **argv = launch_info.GetArguments().GetConstArgumentVector();
265 if (argv) {
266 for (size_t i = 0; argv[i] != NULL; ++i) {
267 if (i == 0)
268 command.Printf(R"( \"%s\")", exe_path);
269 else
270 command.Printf(R"( \"%s\")", argv[i]);
271 }
272 } else {
273 command.Printf(R"( \"%s\")", exe_path);
274 }
275 command.PutCString(" ; echo Process exited with status $?");
276 if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit))
277 command.PutCString(" ; exit");
278
279 StreamString applescript_source;
280
281 applescript_source.Printf(applscript_in_new_tty,
282 command.GetString().str().c_str());
283
284 NSAppleScript *applescript = [[NSAppleScript alloc]
285 initWithSource:[NSString stringWithCString:applescript_source.GetString()
286 .str()
287 .c_str()
288 encoding:NSUTF8StringEncoding]];
289
291
292 Status lldb_error;
293 // Sleep and wait a bit for debugserver to start to listen...
294 ConnectionFileDescriptor file_conn;
295 char connect_url[128];
296 ::snprintf(connect_url, sizeof(connect_url), "unix-accept://%s",
297 unix_socket_name);
298
299 // Spawn a new thread to accept incoming connection on the connect_url
300 // so we can grab the pid from the inferior. We have to do this because we
301 // are sending an AppleScript that will launch a process in Terminal.app,
302 // in a shell and the shell will fork/exec a couple of times before we get
303 // to the process that we wanted to launch. So when our process actually
304 // gets launched, we will handshake with it and get the process ID for it.
305 llvm::Expected<HostThread> accept_thread = ThreadLauncher::LaunchThread(
306 unix_socket_name, [&] { return AcceptPIDFromInferior(connect_url); });
307
308 if (!accept_thread)
309 return Status(accept_thread.takeError());
310
311 [applescript executeAndReturnError:nil];
312
313 thread_result_t accept_thread_result = NULL;
314 lldb_error = accept_thread->Join(&accept_thread_result);
315 if (lldb_error.Success() && accept_thread_result) {
316 pid = (intptr_t)accept_thread_result;
317 }
318
319 llvm::sys::fs::remove(unix_socket_name);
320 [applescript release];
321 if (pid != LLDB_INVALID_PROCESS_ID)
322 launch_info.SetProcessID(pid);
323 return error;
324}
325
326#endif // TARGET_OS_OSX
327
328bool Host::OpenFileInExternalEditor(const FileSpec &file_spec,
329 uint32_t line_no) {
330#if !TARGET_OS_OSX
331 return false;
332#else // !TARGET_OS_OSX
333 // We attach this to an 'odoc' event to specify a particular selection
334 typedef struct {
335 int16_t reserved0; // must be zero
336 int16_t fLineNumber;
337 int32_t fSelStart;
338 int32_t fSelEnd;
339 uint32_t reserved1; // must be zero
340 uint32_t reserved2; // must be zero
341 } BabelAESelInfo;
342
343 Log *log = GetLog(LLDBLog::Host);
344 char file_path[PATH_MAX];
345 file_spec.GetPath(file_path, PATH_MAX);
346 CFCString file_cfstr(file_path, kCFStringEncodingUTF8);
347 CFCReleaser<CFURLRef> file_URL(::CFURLCreateWithFileSystemPath(
348 NULL, file_cfstr.get(), kCFURLPOSIXPathStyle, false));
349
350 LLDB_LOGF(log,
351 "Sending source file: \"%s\" and line: %d to external editor.\n",
352 file_path, line_no);
353
354 long error;
355 BabelAESelInfo file_and_line_info = {
356 0, // reserved0
357 (int16_t)(line_no - 1), // fLineNumber (zero based line number)
358 1, // fSelStart
359 1024, // fSelEnd
360 0, // reserved1
361 0 // reserved2
362 };
363
364 AEKeyDesc file_and_line_desc;
365
366 error = ::AECreateDesc(typeUTF8Text, &file_and_line_info,
367 sizeof(file_and_line_info),
368 &(file_and_line_desc.descContent));
369
370 if (error != noErr) {
371 LLDB_LOGF(log, "Error creating AEDesc: %ld.\n", error);
372 return false;
373 }
374
375 file_and_line_desc.descKey = keyAEPosition;
376
377 static std::string g_app_name;
378 static FSRef g_app_fsref;
379
380 LSApplicationParameters app_params;
381 ::memset(&app_params, 0, sizeof(app_params));
382 app_params.flags =
383 kLSLaunchDefaults | kLSLaunchDontAddToRecents | kLSLaunchDontSwitch;
384
385 char *external_editor = ::getenv("LLDB_EXTERNAL_EDITOR");
386
387 if (external_editor) {
388 LLDB_LOGF(log, "Looking for external editor \"%s\".\n", external_editor);
389
390 if (g_app_name.empty() ||
391 strcmp(g_app_name.c_str(), external_editor) != 0) {
392 CFCString editor_name(external_editor, kCFStringEncodingUTF8);
393 error = ::LSFindApplicationForInfo(kLSUnknownCreator, NULL,
394 editor_name.get(), &g_app_fsref, NULL);
395
396 // If we found the app, then store away the name so we don't have to
397 // re-look it up.
398 if (error != noErr) {
399 LLDB_LOGF(log,
400 "Could not find External Editor application, error: %ld.\n",
401 error);
402 return false;
403 }
404 }
405 app_params.application = &g_app_fsref;
406 }
407
408 ProcessSerialNumber psn;
409 CFCReleaser<CFArrayRef> file_array(
410 CFArrayCreate(NULL, (const void **)file_URL.ptr_address(false), 1, NULL));
411 error = ::LSOpenURLsWithRole(file_array.get(), kLSRolesAll,
412 &file_and_line_desc, &app_params, &psn, 1);
413
414 AEDisposeDesc(&(file_and_line_desc.descContent));
415
416 if (error != noErr) {
417 LLDB_LOGF(log, "LSOpenURLsWithRole failed, error: %ld.\n", error);
418
419 return false;
420 }
421
422 return true;
423#endif // TARGET_OS_OSX
424}
425
427#if !TARGET_OS_OSX
428 return false;
429#else
430 auditinfo_addr_t info;
431 getaudit_addr(&info, sizeof(info));
432 return info.ai_flags & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS;
433#endif
434}
435
436Environment Host::GetEnvironment() { return Environment(*_NSGetEnviron()); }
437
439 if (process_info.ProcessIDIsValid()) {
440 // Make a new mib to stay thread safe
441 int mib[CTL_MAXNAME] = {
442 0,
443 };
444 size_t mib_len = CTL_MAXNAME;
445 if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len))
446 return false;
447
448 mib[mib_len] = process_info.GetProcessID();
449 mib_len++;
450
451 cpu_type_t cpu, sub = 0;
452 size_t len = sizeof(cpu);
453 if (::sysctl(mib, mib_len, &cpu, &len, 0, 0) == 0) {
454 switch (cpu) {
455 case CPU_TYPE_I386:
456 sub = CPU_SUBTYPE_I386_ALL;
457 break;
458 case CPU_TYPE_X86_64:
459 sub = CPU_SUBTYPE_X86_64_ALL;
460 break;
461
462#if defined(CPU_TYPE_ARM64) && defined(CPU_SUBTYPE_ARM64_ALL)
463 case CPU_TYPE_ARM64:
464 sub = CPU_SUBTYPE_ARM64_ALL;
465 break;
466#endif
467
468#if defined(CPU_TYPE_ARM64_32) && defined(CPU_SUBTYPE_ARM64_32_ALL)
470 sub = CPU_SUBTYPE_ARM64_32_ALL;
471 break;
472#endif
473
474 case CPU_TYPE_ARM: {
475 // Note that we fetched the cpu type from the PROCESS but we can't get a
476 // cpusubtype of the
477 // process -- we can only get the host's cpu subtype.
478 uint32_t cpusubtype = 0;
479 len = sizeof(cpusubtype);
480 if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
481 sub = cpusubtype;
482
483 bool host_cpu_is_64bit;
484 uint32_t is64bit_capable;
485 size_t is64bit_capable_len = sizeof(is64bit_capable);
486 host_cpu_is_64bit =
487 sysctlbyname("hw.cpu64bit_capable", &is64bit_capable,
488 &is64bit_capable_len, NULL, 0) == 0;
489
490 // if the host is an armv8 device, its cpusubtype will be in
491 // CPU_SUBTYPE_ARM64 numbering
492 // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value
493 // instead.
494
495 if (host_cpu_is_64bit) {
496 sub = CPU_SUBTYPE_ARM_V7;
497 }
498 } break;
499
500 default:
501 break;
502 }
503 process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, sub);
504 return true;
505 }
506 }
507 process_info.GetArchitecture().Clear();
508 return false;
509}
510
511static bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
512 ProcessInstanceInfo &process_info) {
513 if (process_info.ProcessIDIsValid()) {
514 int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2,
515 (int)process_info.GetProcessID()};
516
517 size_t arg_data_size = 0;
518 if (::sysctl(proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) ||
519 arg_data_size == 0)
520 arg_data_size = 8192;
521
522 // Add a few bytes to the calculated length, I know we need to add at least
523 // one byte
524 // to this number otherwise we get junk back, so add 128 just in case...
525 DataBufferHeap arg_data(arg_data_size + 128, 0);
526 arg_data_size = arg_data.GetByteSize();
527 if (::sysctl(proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size, NULL,
528 0) == 0) {
529 DataExtractor data(arg_data.GetBytes(), arg_data_size,
530 endian::InlHostByteOrder(), sizeof(void *));
531 lldb::offset_t offset = 0;
532 uint32_t argc = data.GetU32(&offset);
533 llvm::Triple &triple = process_info.GetArchitecture().GetTriple();
534 const llvm::Triple::ArchType triple_arch = triple.getArch();
535 const bool check_for_ios_simulator =
536 (triple_arch == llvm::Triple::x86 ||
537 triple_arch == llvm::Triple::x86_64);
538 const char *cstr = data.GetCStr(&offset);
539 if (cstr) {
540 process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
541
542 if (match_info_ptr == NULL ||
544 process_info.GetExecutableFile().GetFilename().GetCString(),
545 match_info_ptr->GetNameMatchType(),
546 match_info_ptr->GetProcessInfo().GetName())) {
547 // Skip NULLs
548 while (true) {
549 const uint8_t *p = data.PeekData(offset, 1);
550 if ((p == NULL) || (*p != '\0'))
551 break;
552 ++offset;
553 }
554 // Now extract all arguments
555 Args &proc_args = process_info.GetArguments();
556 for (int i = 0; i < static_cast<int>(argc); ++i) {
557 cstr = data.GetCStr(&offset);
558 if (cstr)
559 proc_args.AppendArgument(llvm::StringRef(cstr));
560 }
561
562 Environment &proc_env = process_info.GetEnvironment();
563 while ((cstr = data.GetCStr(&offset))) {
564 if (cstr[0] == '\0')
565 break;
566
567 if (check_for_ios_simulator) {
568 if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) ==
569 0)
570 process_info.GetArchitecture().GetTriple().setOS(
571 llvm::Triple::IOS);
572 else
573 process_info.GetArchitecture().GetTriple().setOS(
574 llvm::Triple::MacOSX);
575 }
576
577 proc_env.insert(cstr);
578 }
579 return true;
580 }
581 }
582 }
583 }
584 return false;
585}
586
588 if (process_info.ProcessIDIsValid()) {
589 int mib[4];
590 mib[0] = CTL_KERN;
591 mib[1] = KERN_PROC;
592 mib[2] = KERN_PROC_PID;
593 mib[3] = process_info.GetProcessID();
594 struct kinfo_proc proc_kinfo;
595 size_t proc_kinfo_size = sizeof(struct kinfo_proc);
596
597 if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
598 if (proc_kinfo_size > 0) {
599 process_info.SetParentProcessID(proc_kinfo.kp_eproc.e_ppid);
600 process_info.SetUserID(proc_kinfo.kp_eproc.e_pcred.p_ruid);
601 process_info.SetGroupID(proc_kinfo.kp_eproc.e_pcred.p_rgid);
602 process_info.SetEffectiveUserID(proc_kinfo.kp_eproc.e_ucred.cr_uid);
603 if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
604 process_info.SetEffectiveGroupID(
605 proc_kinfo.kp_eproc.e_ucred.cr_groups[0]);
606 else
607 process_info.SetEffectiveGroupID(UINT32_MAX);
608 return true;
609 }
610 }
611 }
613 process_info.SetUserID(UINT32_MAX);
614 process_info.SetGroupID(UINT32_MAX);
615 process_info.SetEffectiveUserID(UINT32_MAX);
616 process_info.SetEffectiveGroupID(UINT32_MAX);
617 return false;
618}
619
621 ProcessInstanceInfoList &process_infos) {
622 std::vector<struct kinfo_proc> kinfos;
623
624 int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
625
626 size_t pid_data_size = 0;
627 if (::sysctl(mib, 3, nullptr, &pid_data_size, nullptr, 0) != 0)
628 return 0;
629
630 // Add a few extra in case a few more show up
631 const size_t estimated_pid_count =
632 (pid_data_size / sizeof(struct kinfo_proc)) + 10;
633
634 kinfos.resize(estimated_pid_count);
635 pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
636
637 if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, nullptr, 0) != 0)
638 return 0;
639
640 const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
641
642 bool all_users = match_info.GetMatchAllUsers();
643 const lldb::pid_t our_pid = getpid();
644 const uid_t our_uid = getuid();
645 for (size_t i = 0; i < actual_pid_count; i++) {
646 const struct kinfo_proc &kinfo = kinfos[i];
647
648 bool kinfo_user_matches = false;
649 if (all_users)
650 kinfo_user_matches = true;
651 else
652 kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid;
653
654 // Special case, if lldb is being run as root we can attach to anything.
655 if (our_uid == 0)
656 kinfo_user_matches = true;
657
658 if (!kinfo_user_matches || // Make sure the user is acceptable
659 static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) ==
660 our_pid || // Skip this process
661 kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero)
662 kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains...
663 kinfo.kp_proc.p_flag & P_TRACED || // Being debugged?
664 kinfo.kp_proc.p_flag & P_WEXIT)
665 continue;
666
667 ProcessInstanceInfo process_info;
668 process_info.SetProcessID(kinfo.kp_proc.p_pid);
669 process_info.SetParentProcessID(kinfo.kp_eproc.e_ppid);
670 process_info.SetUserID(kinfo.kp_eproc.e_pcred.p_ruid);
671 process_info.SetGroupID(kinfo.kp_eproc.e_pcred.p_rgid);
672 process_info.SetEffectiveUserID(kinfo.kp_eproc.e_ucred.cr_uid);
673 if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
674 process_info.SetEffectiveGroupID(kinfo.kp_eproc.e_ucred.cr_groups[0]);
675 else
676 process_info.SetEffectiveGroupID(UINT32_MAX);
677
678 // Make sure our info matches before we go fetch the name and cpu type
679 if (!match_info.UserIDsMatch(process_info) ||
680 !match_info.ProcessIDsMatch(process_info))
681 continue;
682
683 // Get CPU type first so we can know to look for iOS simulator is we have
684 // x86 or x86_64
685 if (GetMacOSXProcessCPUType(process_info)) {
686 if (GetMacOSXProcessArgs(&match_info, process_info)) {
687 if (match_info.Matches(process_info))
688 process_infos.push_back(process_info);
689 }
690 }
691 }
692 return process_infos.size();
693}
694
696 process_info.SetProcessID(pid);
697 bool success = false;
698
699 // Get CPU type first so we can know to look for iOS simulator is we have x86
700 // or x86_64
701 if (GetMacOSXProcessCPUType(process_info))
702 success = true;
703
704 if (GetMacOSXProcessArgs(NULL, process_info))
705 success = true;
706
707 if (GetMacOSXProcessUserAndGroup(process_info))
708 success = true;
709
710 if (success)
711 return true;
712
713 process_info.Clear();
714 return false;
715}
716
717#if TARGET_OS_OSX
718static void PackageXPCArguments(xpc_object_t message, const char *prefix,
719 const Args &args) {
720 size_t count = args.GetArgumentCount();
721 char buf[50]; // long enough for 'argXXX'
722 memset(buf, 0, 50);
723 sprintf(buf, "%sCount", prefix);
724 xpc_dictionary_set_int64(message, buf, count);
725 for (size_t i = 0; i < count; i++) {
726 memset(buf, 0, 50);
727 sprintf(buf, "%s%zi", prefix, i);
728 xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i));
729 }
730}
731
732static void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix,
733 const Environment &env) {
734 xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(),
735 env.size());
736 size_t i = 0;
737 for (const auto &KV : env) {
738 xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(),
739 Environment::compose(KV).c_str());
740 }
741}
742
743/*
744 A valid authorizationRef means that
745 - there is the LaunchUsingXPCRightName rights in the /etc/authorization
746 - we have successfully copied the rights to be send over the XPC wire
747 Once obtained, it will be valid for as long as the process lives.
748 */
749static AuthorizationRef authorizationRef = NULL;
750static Status getXPCAuthorization(ProcessLaunchInfo &launch_info) {
753
754 if ((launch_info.GetUserID() == 0) && !authorizationRef) {
755 OSStatus createStatus =
756 AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
757 kAuthorizationFlagDefaults, &authorizationRef);
758 if (createStatus != errAuthorizationSuccess) {
759 error.SetError(1, eErrorTypeGeneric);
760 error.SetErrorString("Can't create authorizationRef.");
761 LLDB_LOG(log, "error: {0}", error);
762 return error;
763 }
764
765 OSStatus rightsStatus =
766 AuthorizationRightGet(LaunchUsingXPCRightName, NULL);
767 if (rightsStatus != errAuthorizationSuccess) {
768 // No rights in the security database, Create it with the right prompt.
769 CFStringRef prompt =
770 CFSTR("Xcode is trying to take control of a root process.");
771 CFStringRef keys[] = {CFSTR("en")};
772 CFTypeRef values[] = {prompt};
773 CFDictionaryRef promptDict = CFDictionaryCreate(
774 kCFAllocatorDefault, (const void **)keys, (const void **)values, 1,
775 &kCFCopyStringDictionaryKeyCallBacks,
776 &kCFTypeDictionaryValueCallBacks);
777
778 CFStringRef keys1[] = {CFSTR("class"), CFSTR("group"), CFSTR("comment"),
779 CFSTR("default-prompt"), CFSTR("shared")};
780 CFTypeRef values1[] = {CFSTR("user"), CFSTR("admin"),
781 CFSTR(LaunchUsingXPCRightName), promptDict,
782 kCFBooleanFalse};
783 CFDictionaryRef dict = CFDictionaryCreate(
784 kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5,
785 &kCFCopyStringDictionaryKeyCallBacks,
786 &kCFTypeDictionaryValueCallBacks);
787 rightsStatus = AuthorizationRightSet(
788 authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL);
789 CFRelease(promptDict);
790 CFRelease(dict);
791 }
792
793 OSStatus copyRightStatus = errAuthorizationDenied;
794 if (rightsStatus == errAuthorizationSuccess) {
795 AuthorizationItem item1 = {LaunchUsingXPCRightName, 0, NULL, 0};
796 AuthorizationItem items[] = {item1};
797 AuthorizationRights requestedRights = {1, items};
798 AuthorizationFlags authorizationFlags =
799 kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
800 copyRightStatus = AuthorizationCopyRights(
801 authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment,
802 authorizationFlags, NULL);
803 }
804
805 if (copyRightStatus != errAuthorizationSuccess) {
806 // Eventually when the commandline supports running as root and the user
807 // is not
808 // logged in in the current audit session, we will need the trick in gdb
809 // where
810 // we ask the user to type in the root passwd in the terminal.
811 error.SetError(2, eErrorTypeGeneric);
812 error.SetErrorStringWithFormat(
813 "Launching as root needs root authorization.");
814 LLDB_LOG(log, "error: {0}", error);
815
816 if (authorizationRef) {
817 AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
818 authorizationRef = NULL;
819 }
820 }
821 }
822
823 return error;
824}
825#endif
826
827static short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) {
828 short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
829
830 if (launch_info.GetFlags().Test(eLaunchFlagExec))
831 flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag
832
833 if (launch_info.GetFlags().Test(eLaunchFlagDebug))
834 flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag
835
836 if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
837 flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag
838
839 if (launch_info.GetLaunchInSeparateProcessGroup())
840 flags |= POSIX_SPAWN_SETPGROUP;
841
842#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
843#if defined(__x86_64__) || defined(__i386__)
844 static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate;
845 if (g_use_close_on_exec_flag == eLazyBoolCalculate) {
846 g_use_close_on_exec_flag = eLazyBoolNo;
847
848 llvm::VersionTuple version = HostInfo::GetOSVersion();
849 if (version > llvm::VersionTuple(10, 7)) {
850 // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or
851 // earlier
852 g_use_close_on_exec_flag = eLazyBoolYes;
853 }
854 }
855#else
856 static LazyBool g_use_close_on_exec_flag = eLazyBoolYes;
857#endif // defined(__x86_64__) || defined(__i386__)
858 // Close all files exception those with file actions if this is supported.
859 if (g_use_close_on_exec_flag == eLazyBoolYes)
860 flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
861#endif // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
862 return flags;
863}
864
865static Status LaunchProcessXPC(const char *exe_path,
866 ProcessLaunchInfo &launch_info,
867 lldb::pid_t &pid) {
868#if TARGET_OS_OSX
869 Status error = getXPCAuthorization(launch_info);
870 if (error.Fail())
871 return error;
872
874
875 uid_t requested_uid = launch_info.GetUserID();
876 const char *xpc_service = nil;
877 bool send_auth = false;
878 AuthorizationExternalForm extForm;
879 if (requested_uid == 0) {
880 if (AuthorizationMakeExternalForm(authorizationRef, &extForm) ==
881 errAuthorizationSuccess) {
882 send_auth = true;
883 } else {
884 error.SetError(3, eErrorTypeGeneric);
885 error.SetErrorStringWithFormat("Launching root via XPC needs to "
886 "externalize authorization reference.");
887 LLDB_LOG(log, "error: {0}", error);
888 return error;
889 }
890 xpc_service = LaunchUsingXPCRightName;
891 } else {
892 error.SetError(4, eErrorTypeGeneric);
893 error.SetErrorStringWithFormat(
894 "Launching via XPC is only currently available for root.");
895 LLDB_LOG(log, "error: {0}", error);
896 return error;
897 }
898
899 xpc_connection_t conn = xpc_connection_create(xpc_service, NULL);
900
901 xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
902 xpc_type_t type = xpc_get_type(event);
903
904 if (type == XPC_TYPE_ERROR) {
905 if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
906 // The service has either canceled itself, crashed, or been terminated.
907 // The XPC connection is still valid and sending a message to it will
908 // re-launch the service.
909 // If the service is state-full, this is the time to initialize the new
910 // service.
911 return;
912 } else if (event == XPC_ERROR_CONNECTION_INVALID) {
913 // The service is invalid. Either the service name supplied to
914 // xpc_connection_create() is incorrect
915 // or we (this process) have canceled the service; we can do any cleanup
916 // of application state at this point.
917 // printf("Service disconnected");
918 return;
919 } else {
920 // printf("Unexpected error from service: %s",
921 // xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
922 }
923
924 } else {
925 // printf("Received unexpected event in handler");
926 }
927 });
928
929 xpc_connection_set_finalizer_f(conn, xpc_finalizer_t(xpc_release));
930 xpc_connection_resume(conn);
931 xpc_object_t message = xpc_dictionary_create(nil, nil, 0);
932
933 if (send_auth) {
934 xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes,
935 sizeof(AuthorizationExternalForm));
936 }
937
938 PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey,
939 launch_info.GetArguments());
940 PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey,
941 launch_info.GetEnvironment());
942
943 // Posix spawn stuff.
944 xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey,
945 launch_info.GetArchitecture().GetMachOCPUType());
946 xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey,
947 GetPosixspawnFlags(launch_info));
948 const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
949 if (file_action && !file_action->GetPath().empty()) {
950 xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey,
951 file_action->GetPath().str().c_str());
952 }
953 file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
954 if (file_action && !file_action->GetPath().empty()) {
955 xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey,
956 file_action->GetPath().str().c_str());
957 }
958 file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
959 if (file_action && !file_action->GetPath().empty()) {
960 xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey,
961 file_action->GetPath().str().c_str());
962 }
963
964 xpc_object_t reply =
965 xpc_connection_send_message_with_reply_sync(conn, message);
966 xpc_type_t returnType = xpc_get_type(reply);
967 if (returnType == XPC_TYPE_DICTIONARY) {
968 pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey);
969 if (pid == 0) {
970 int errorType =
971 xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey);
972 int errorCode =
973 xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey);
974
975 error.SetError(errorCode, eErrorTypeGeneric);
976 error.SetErrorStringWithFormat(
977 "Problems with launching via XPC. Error type : %i, code : %i",
978 errorType, errorCode);
979 LLDB_LOG(log, "error: {0}", error);
980
981 if (authorizationRef) {
982 AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
983 authorizationRef = NULL;
984 }
985 }
986 } else if (returnType == XPC_TYPE_ERROR) {
987 error.SetError(5, eErrorTypeGeneric);
988 error.SetErrorStringWithFormat(
989 "Problems with launching via XPC. XPC error : %s",
990 xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION));
991 LLDB_LOG(log, "error: {0}", error);
992 }
993
994 return error;
995#else
997 return error;
998#endif
999}
1000
1001static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info,
1002 Log *log, Status &error) {
1003 if (info == NULL)
1004 return false;
1005
1006 posix_spawn_file_actions_t *file_actions =
1007 static_cast<posix_spawn_file_actions_t *>(_file_actions);
1008
1009 switch (info->GetAction()) {
1011 error.Clear();
1012 break;
1013
1015 if (info->GetFD() == -1)
1016 error.SetErrorString(
1017 "invalid fd for posix_spawn_file_actions_addclose(...)");
1018 else {
1019 error.SetError(
1020 ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()),
1022 if (error.Fail())
1023 LLDB_LOG(log,
1024 "error: {0}, posix_spawn_file_actions_addclose "
1025 "(action={1}, fd={2})",
1026 error, file_actions, info->GetFD());
1027 }
1028 break;
1029
1031 if (info->GetFD() == -1)
1032 error.SetErrorString(
1033 "invalid fd for posix_spawn_file_actions_adddup2(...)");
1034 else if (info->GetActionArgument() == -1)
1035 error.SetErrorString(
1036 "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)");
1037 else {
1038 error.SetError(
1039 ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(),
1040 info->GetActionArgument()),
1042 if (error.Fail())
1043 LLDB_LOG(log,
1044 "error: {0}, posix_spawn_file_actions_adddup2 "
1045 "(action={1}, fd={2}, dup_fd={3})",
1046 error, file_actions, info->GetFD(), info->GetActionArgument());
1047 }
1048 break;
1049
1051 if (info->GetFD() == -1)
1052 error.SetErrorString(
1053 "invalid fd in posix_spawn_file_actions_addopen(...)");
1054 else {
1055 int oflag = info->GetActionArgument();
1056
1057 mode_t mode = 0;
1058
1059 if (oflag & O_CREAT)
1060 mode = 0640;
1061
1062 error.SetError(::posix_spawn_file_actions_addopen(
1063 file_actions, info->GetFD(),
1064 info->GetPath().str().c_str(), oflag, mode),
1066 if (error.Fail())
1067 LLDB_LOG(log,
1068 "error: {0}, posix_spawn_file_actions_addopen (action={1}, "
1069 "fd={2}, path='{3}', oflag={4}, mode={5})",
1070 error, file_actions, info->GetFD(), info->GetPath(), oflag,
1071 mode);
1072 }
1073 break;
1074 }
1075 return error.Success();
1076}
1077
1078static Status LaunchProcessPosixSpawn(const char *exe_path,
1079 const ProcessLaunchInfo &launch_info,
1080 lldb::pid_t &pid) {
1081 Status error;
1083
1084 posix_spawnattr_t attr;
1085 error.SetError(::posix_spawnattr_init(&attr), eErrorTypePOSIX);
1086
1087 if (error.Fail()) {
1088 LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error);
1089 return error;
1090 }
1091
1092 // Make sure we clean up the posix spawn attributes before exiting this scope.
1093 auto cleanup_attr =
1094 llvm::make_scope_exit([&]() { posix_spawnattr_destroy(&attr); });
1095
1096 sigset_t no_signals;
1097 sigset_t all_signals;
1098 sigemptyset(&no_signals);
1099 sigfillset(&all_signals);
1100 ::posix_spawnattr_setsigmask(&attr, &no_signals);
1101 ::posix_spawnattr_setsigdefault(&attr, &all_signals);
1102
1103 short flags = GetPosixspawnFlags(launch_info);
1104
1105 error.SetError(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX);
1106 if (error.Fail()) {
1107 LLDB_LOG(log,
1108 "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )",
1109 error, flags);
1110 return error;
1111 }
1112
1113 bool is_graphical = true;
1114
1115#if TARGET_OS_OSX
1116 SecuritySessionId session_id;
1117 SessionAttributeBits session_attributes;
1118 OSStatus status =
1119 SessionGetInfo(callerSecuritySession, &session_id, &session_attributes);
1120 if (status == errSessionSuccess)
1121 is_graphical = session_attributes & sessionHasGraphicAccess;
1122#endif
1123
1124 // When lldb is ran through a graphical session, make the debuggee process
1125 // responsible for its own TCC permissions instead of inheriting them from
1126 // its parent.
1127 if (is_graphical && launch_info.GetFlags().Test(eLaunchFlagDebug) &&
1128 !launch_info.GetFlags().Test(eLaunchFlagInheritTCCFromParent)) {
1130 if (error.Fail()) {
1131 LLDB_LOG(log, "error: {0}, setup_posix_spawn_responsible_flag(&attr)",
1132 error);
1133 return error;
1134 }
1135 }
1136
1137 // Don't set the binpref if a shell was provided. After all, that's only
1138 // going to affect what version of the shell is launched, not what fork of
1139 // the binary is launched. We insert "arch --arch <ARCH> as part of the
1140 // shell invocation to do that job on OSX.
1141 if (launch_info.GetShell() == FileSpec()) {
1142 const ArchSpec &arch_spec = launch_info.GetArchitecture();
1143 cpu_type_t cpu_type = arch_spec.GetMachOCPUType();
1144 cpu_type_t cpu_subtype = arch_spec.GetMachOCPUSubType();
1145 const bool set_cpu_type =
1146 cpu_type != 0 && cpu_type != static_cast<cpu_type_t>(UINT32_MAX) &&
1147 cpu_type != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE);
1148 const bool set_cpu_subtype =
1149 cpu_subtype != 0 &&
1150 cpu_subtype != static_cast<cpu_subtype_t>(UINT32_MAX) &&
1151 cpu_subtype != CPU_SUBTYPE_X86_64_H;
1152 if (set_cpu_type) {
1153 size_t ocount = 0;
1154 typedef int (*posix_spawnattr_setarchpref_np_t)(
1155 posix_spawnattr_t *, size_t, cpu_type_t *, cpu_subtype_t *, size_t *);
1156 posix_spawnattr_setarchpref_np_t posix_spawnattr_setarchpref_np_fn =
1157 (posix_spawnattr_setarchpref_np_t)dlsym(
1158 RTLD_DEFAULT, "posix_spawnattr_setarchpref_np");
1159 if (set_cpu_subtype && posix_spawnattr_setarchpref_np_fn) {
1160 error.SetError((*posix_spawnattr_setarchpref_np_fn)(
1161 &attr, 1, &cpu_type, &cpu_subtype, &ocount),
1163 if (error.Fail())
1164 LLDB_LOG(log,
1165 "error: {0}, ::posix_spawnattr_setarchpref_np ( &attr, 1, "
1166 "cpu_type = {1:x}, cpu_subtype = {1:x}, count => {2} )",
1167 error, cpu_type, cpu_subtype, ocount);
1168
1169 if (error.Fail() || ocount != 1)
1170 return error;
1171 } else {
1172 error.SetError(
1173 ::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount),
1175 if (error.Fail())
1176 LLDB_LOG(log,
1177 "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, "
1178 "cpu_type = {1:x}, count => {2} )",
1179 error, cpu_type, ocount);
1180 if (error.Fail() || ocount != 1)
1181 return error;
1182 }
1183 }
1184 }
1185
1186 const char *tmp_argv[2];
1187 char *const *argv = const_cast<char *const *>(
1188 launch_info.GetArguments().GetConstArgumentVector());
1189 Environment::Envp envp = launch_info.GetEnvironment().getEnvp();
1190 if (argv == NULL) {
1191 // posix_spawn gets very unhappy if it doesn't have at least the program
1192 // name in argv[0]. One of the side affects I have noticed is the
1193 // environment
1194 // variables don't make it into the child process if "argv == NULL"!!!
1195 tmp_argv[0] = exe_path;
1196 tmp_argv[1] = NULL;
1197 argv = const_cast<char *const *>(tmp_argv);
1198 }
1199
1200 FileSpec working_dir{launch_info.GetWorkingDirectory()};
1201 if (working_dir) {
1202 // Set the working directory on this thread only
1203 std::string working_dir_path = working_dir.GetPath();
1204 if (__pthread_chdir(working_dir_path.c_str()) < 0) {
1205 if (errno == ENOENT) {
1206 error.SetErrorStringWithFormat("No such file or directory: %s",
1207 working_dir_path.c_str());
1208 } else if (errno == ENOTDIR) {
1209 error.SetErrorStringWithFormat("Path doesn't name a directory: %s",
1210 working_dir_path.c_str());
1211 } else {
1212 error.SetErrorStringWithFormat("An unknown error occurred when "
1213 "changing directory for process "
1214 "execution.");
1215 }
1216 return error;
1217 }
1218 }
1219
1220 ::pid_t result_pid = LLDB_INVALID_PROCESS_ID;
1221 const size_t num_file_actions = launch_info.GetNumFileActions();
1222 if (num_file_actions > 0) {
1223 posix_spawn_file_actions_t file_actions;
1224 error.SetError(::posix_spawn_file_actions_init(&file_actions),
1226 if (error.Fail()) {
1227 LLDB_LOG(log,
1228 "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )",
1229 error);
1230 return error;
1231 }
1232
1233 // Make sure we clean up the posix file actions before exiting this scope.
1234 auto cleanup_fileact = llvm::make_scope_exit(
1235 [&]() { posix_spawn_file_actions_destroy(&file_actions); });
1236
1237 for (size_t i = 0; i < num_file_actions; ++i) {
1238 const FileAction *launch_file_action =
1239 launch_info.GetFileActionAtIndex(i);
1240 if (launch_file_action) {
1241 if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log,
1242 error))
1243 return error;
1244 }
1245 }
1246
1247 error.SetError(
1248 ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp),
1250
1251 if (error.Fail()) {
1252 LLDB_LOG(log,
1253 "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', "
1254 "file_actions = {3}, "
1255 "attr = {4}, argv = {5}, envp = {6} )",
1256 error, result_pid, exe_path, &file_actions, &attr, argv,
1257 envp.get());
1258 if (log) {
1259 for (int ii = 0; argv[ii]; ++ii)
1260 LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
1261 }
1262 }
1263
1264 } else {
1265 error.SetError(
1266 ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp),
1268
1269 if (error.Fail()) {
1270 LLDB_LOG(log,
1271 "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', "
1272 "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )",
1273 error, result_pid, exe_path, &attr, argv, envp.get());
1274 if (log) {
1275 for (int ii = 0; argv[ii]; ++ii)
1276 LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
1277 }
1278 }
1279 }
1280 pid = result_pid;
1281
1282 if (working_dir) {
1283 // No more thread specific current working directory
1284 __pthread_fchdir(-1);
1285 }
1286
1287 return error;
1288}
1289
1290static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) {
1291 bool result = false;
1292
1293#if TARGET_OS_OSX
1294 bool launchingAsRoot = launch_info.GetUserID() == 0;
1295 bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0;
1296
1297 if (launchingAsRoot && !currentUserIsRoot) {
1298 // If current user is already root, we don't need XPC's help.
1299 result = true;
1300 }
1301#endif
1302
1303 return result;
1304}
1305
1307 Status error;
1308
1310 FileSpec exe_spec(launch_info.GetExecutableFile());
1311
1312 if (!fs.Exists(exe_spec))
1313 FileSystem::Instance().Resolve(exe_spec);
1314
1315 if (!fs.Exists(exe_spec))
1317
1318 if (!fs.Exists(exe_spec)) {
1319 error.SetErrorStringWithFormatv("executable doesn't exist: '{0}'",
1320 exe_spec);
1321 return error;
1322 }
1323
1324 if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) {
1325#if TARGET_OS_OSX
1326 return LaunchInNewTerminalWithAppleScript(exe_spec.GetPath().c_str(),
1327 launch_info);
1328#else
1329 error.SetErrorString("launching a process in a new terminal is not "
1330 "supported on iOS devices");
1331 return error;
1332#endif
1333 }
1334
1336
1337 auto exe_path = exe_spec.GetPath();
1338
1339 if (ShouldLaunchUsingXPC(launch_info))
1340 error = LaunchProcessXPC(exe_path.c_str(), launch_info, pid);
1341 else
1342 error = LaunchProcessPosixSpawn(exe_path.c_str(), launch_info, pid);
1343
1344 if (pid != LLDB_INVALID_PROCESS_ID) {
1345 // If all went well, then set the process ID into the launch info
1346 launch_info.SetProcessID(pid);
1347
1348 // Make sure we reap any processes we spawn or we will have zombies.
1349 bool monitoring = launch_info.MonitorProcess();
1350 UNUSED_IF_ASSERT_DISABLED(monitoring);
1351 assert(monitoring);
1352 } else {
1353 // Invalid process ID, something didn't go well
1354 if (error.Success())
1355 error.SetErrorString("process launch failed for unknown reasons");
1356 }
1357 return error;
1358}
1359
1361 Status error;
1362 if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
1363 FileSpec expand_tool_spec = HostInfo::GetSupportExeDir();
1364 if (!expand_tool_spec) {
1365 error.SetErrorString(
1366 "could not get support executable directory for lldb-argdumper tool");
1367 return error;
1368 }
1369 expand_tool_spec.AppendPathComponent("lldb-argdumper");
1370 if (!FileSystem::Instance().Exists(expand_tool_spec)) {
1371 error.SetErrorStringWithFormat(
1372 "could not find the lldb-argdumper tool: %s",
1373 expand_tool_spec.GetPath().c_str());
1374 return error;
1375 }
1376
1377 StreamString expand_tool_spec_stream;
1378 expand_tool_spec_stream.Printf("\"%s\"",
1379 expand_tool_spec.GetPath().c_str());
1380
1381 Args expand_command(expand_tool_spec_stream.GetData());
1382 expand_command.AppendArguments(launch_info.GetArguments());
1383
1384 int status;
1385 std::string output;
1386 FileSpec cwd(launch_info.GetWorkingDirectory());
1387 if (!FileSystem::Instance().Exists(cwd)) {
1388 char *wd = getcwd(nullptr, 0);
1389 if (wd == nullptr) {
1390 error.SetErrorStringWithFormat(
1391 "cwd does not exist; cannot launch with shell argument expansion");
1392 return error;
1393 } else {
1394 FileSpec working_dir(wd);
1395 free(wd);
1396 launch_info.SetWorkingDirectory(working_dir);
1397 }
1398 }
1399 bool run_in_shell = true;
1400 bool hide_stderr = true;
1401 Status e =
1402 RunShellCommand(expand_command, cwd, &status, nullptr, &output,
1403 std::chrono::seconds(10), run_in_shell, hide_stderr);
1404
1405 if (e.Fail())
1406 return e;
1407
1408 if (status != 0) {
1409 error.SetErrorStringWithFormat("lldb-argdumper exited with error %d",
1410 status);
1411 return error;
1412 }
1413
1414 auto data_sp = StructuredData::ParseJSON(output);
1415 if (!data_sp) {
1416 error.SetErrorString("invalid JSON");
1417 return error;
1418 }
1419
1420 auto dict_sp = data_sp->GetAsDictionary();
1421 if (!data_sp) {
1422 error.SetErrorString("invalid JSON");
1423 return error;
1424 }
1425
1426 auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
1427 if (!args_sp) {
1428 error.SetErrorString("invalid JSON");
1429 return error;
1430 }
1431
1432 auto args_array_sp = args_sp->GetAsArray();
1433 if (!args_array_sp) {
1434 error.SetErrorString("invalid JSON");
1435 return error;
1436 }
1437
1438 launch_info.GetArguments().Clear();
1439
1440 for (size_t i = 0; i < args_array_sp->GetSize(); i++) {
1441 auto item_sp = args_array_sp->GetItemAtIndex(i);
1442 if (!item_sp)
1443 continue;
1444 auto str_sp = item_sp->GetAsString();
1445 if (!str_sp)
1446 continue;
1447
1448 launch_info.GetArguments().AppendArgument(str_sp->GetValue());
1449 }
1450 }
1451
1452 return error;
1453}
1454
1455llvm::Expected<HostThread> Host::StartMonitoringChildProcess(
1456 const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) {
1457 unsigned long mask = DISPATCH_PROC_EXIT;
1458
1460
1461 dispatch_source_t source = ::dispatch_source_create(
1462 DISPATCH_SOURCE_TYPE_PROC, pid, mask,
1463 ::dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
1464
1465 LLDB_LOGF(log,
1466 "Host::StartMonitoringChildProcess(callback, pid=%i) source = %p\n",
1467 static_cast<int>(pid), static_cast<void *>(source));
1468
1469 if (source) {
1470 Host::MonitorChildProcessCallback callback_copy = callback;
1471 ::dispatch_source_set_cancel_handler(source, ^{
1472 dispatch_release(source);
1473 });
1474 ::dispatch_source_set_event_handler(source, ^{
1475
1476 int status = 0;
1477 int wait_pid = 0;
1478 wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, 0);
1479 if (wait_pid >= 0) {
1480 int signal = 0;
1481 int exit_status = 0;
1482 const char *status_cstr = NULL;
1483 if (WIFEXITED(status)) {
1484 exit_status = WEXITSTATUS(status);
1485 status_cstr = "EXITED";
1486 } else if (WIFSIGNALED(status)) {
1487 signal = WTERMSIG(status);
1488 status_cstr = "SIGNALED";
1489 exit_status = -1;
1490 } else {
1491 llvm_unreachable("Unknown status");
1492 }
1493
1494 LLDB_LOGF(log,
1495 "::waitpid (pid = %llu, &status, 0) => pid = %i, status "
1496 "= 0x%8.8x (%s), signal = %i, exit_status = %i",
1497 pid, wait_pid, status, status_cstr, signal, exit_status);
1498
1499 if (callback_copy)
1500 callback_copy(pid, signal, exit_status);
1501
1502 ::dispatch_source_cancel(source);
1503 }
1504 });
1505
1506 ::dispatch_resume(source);
1507 }
1508 return HostThread();
1509}
static llvm::raw_ostream & error(Stream &strm)
#define CPU_SUBTYPE_X86_64_H
#define CPU_TYPE_ARM64
#define CPU_TYPE_ARM64_32
static Status LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid)
Definition: Host.mm:1078
static os_log_t g_os_log
Definition: Host.mm:102
static Status LaunchProcessXPC(const char *exe_path, ProcessLaunchInfo &launch_info, lldb::pid_t &pid)
Definition: Host.mm:865
static short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info)
Definition: Host.mm:827
#define _POSIX_SPAWN_DISABLE_ASLR
Definition: Host.mm:91
int __pthread_chdir(const char *path)
static bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info)
Definition: Host.mm:587
static std::once_flag g_os_log_once
Definition: Host.mm:103
static bool GetMacOSXProcessCPUType(ProcessInstanceInfo &process_info)
Definition: Host.mm:438
int __pthread_fchdir(int fildes)
static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info)
Definition: Host.mm:1290
static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *log, Status &error)
Definition: Host.mm:1001
static bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr, ProcessInstanceInfo &process_info)
Definition: Host.mm:511
int cpu_subtype_t
int cpu_type_t
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:337
#define LLDB_LOGF(log,...)
Definition: Log.h:344
static int setup_posix_spawn_responsible_flag(posix_spawnattr_t *attr)
An architecture specification class.
Definition: ArchSpec.h:32
bool IsValid() const
Tests if this ArchSpec is valid.
Definition: ArchSpec.h:361
void Clear()
Clears the object state.
Definition: ArchSpec.cpp:536
llvm::Triple & GetTriple()
Architecture triple accessor.
Definition: ArchSpec.h:463
bool SetArchitecture(ArchitectureType arch_type, uint32_t cpu, uint32_t sub, uint32_t os=0)
Change the architecture object type, CPU type and OS type.
Definition: ArchSpec.cpp:854
uint32_t GetMachOCPUSubType() const
Definition: ArchSpec.cpp:658
uint32_t GetMachOCPUType() const
Definition: ArchSpec.cpp:646
Core GetCore() const
Definition: ArchSpec.h:442
const char * GetArchitectureName() const
Returns a static string representing the current architecture.
Definition: ArchSpec.cpp:547
A command line argument class.
Definition: Args.h:33
size_t GetArgumentCount() const
Gets the number of arguments left in this command object.
Definition: Args.h:116
void AppendArgument(llvm::StringRef arg_str, char quote_char='\0')
Appends a new argument to the end of the list argument list.
Definition: Args.cpp:323
const char * GetArgumentAtIndex(size_t idx) const
Gets the NULL terminated C string argument pointer for the argument at index idx.
Definition: Args.cpp:264
const char ** GetConstArgumentVector() const
Gets the argument vector.
Definition: Args.cpp:280
void Clear()
Clear the arguments.
Definition: Args.cpp:379
lldb::ConnectionStatus Connect(llvm::StringRef url, Status *error_ptr) override
Connect using the connect string url.
size_t Read(void *dst, size_t dst_len, const Timeout< std::micro > &timeout, lldb::ConnectionStatus &status, Status *error_ptr) override
The read function that attempts to read from the connection.
const char * GetCString() const
Get the string value as a C string.
Definition: ConstString.h:215
A subclass of DataBuffer that stores a data buffer on the heap.
lldb::offset_t GetByteSize() const override
An data extractor class.
Definition: DataExtractor.h:48
const char * GetCStr(lldb::offset_t *offset_ptr) const
Extract a C string from *offset_ptr.
const uint8_t * PeekData(lldb::offset_t offset, lldb::offset_t length) const
Peek at a bytes at offset.
uint32_t GetU32(lldb::offset_t *offset_ptr) const
Extract a uint32_t value from *offset_ptr.
char *const * get() const
Definition: Environment.h:27
static std::string compose(const value_type &KeyValue)
Definition: Environment.h:80
std::pair< iterator, bool > insert(llvm::StringRef KeyEqValue)
Definition: Environment.h:71
Action GetAction() const
Definition: FileAction.h:38
llvm::StringRef GetPath() const
Definition: FileAction.cpp:28
int GetActionArgument() const
Definition: FileAction.h:40
A file utility class.
Definition: FileSpec.h:56
void SetFile(llvm::StringRef path, Style style)
Change the file specified with a new path.
Definition: FileSpec.cpp:173
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:453
const ConstString & GetFilename() const
Filename string const get accessor.
Definition: FileSpec.h:240
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:366
void Clear()
Clears the object state.
Definition: FileSpec.cpp:258
void SetFilename(ConstString filename)
Filename string set accessor.
Definition: FileSpec.cpp:344
void Resolve(llvm::SmallVectorImpl< char > &path)
Resolve path to make it canonical.
bool ResolveExecutableLocation(FileSpec &file_spec)
Call into the Host to see if it can help find the file.
bool Exists(const FileSpec &file_spec) const
Returns whether the given file exists.
static FileSystem & Instance()
bool Test(ValueType bit) const
Test a single flag bit.
Definition: Flags.h:96
static Status LaunchProcess(ProcessLaunchInfo &launch_info)
Launch the process specified in launch_info.
static bool OpenFileInExternalEditor(const FileSpec &file_spec, uint32_t line_no)
static bool ResolveExecutableInBundle(FileSpec &file)
When executable files may live within a directory, where the directory represents an executable bundl...
static Status ShellExpandArguments(ProcessLaunchInfo &launch_info)
Perform expansion of the command-line for this launch info This can potentially involve wildcard expa...
static Status RunShellCommand(llvm::StringRef command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout< std::micro > &timeout, bool run_in_shell=true, bool hide_stderr=false)
Run a shell command.
static Environment GetEnvironment()
static void SystemLog(llvm::StringRef message)
Emit the given message to the operating system log.
Definition: common/Host.cpp:92
static uint32_t FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &proc_infos)
static bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info)
std::function< void(lldb::pid_t pid, int signal, int status)> MonitorChildProcessCallback
Definition: Host.h:69
static llvm::Expected< HostThread > StartMonitoringChildProcess(const MonitorChildProcessCallback &callback, lldb::pid_t pid)
Start monitoring a child process.
static bool IsInteractiveGraphicSession()
Check if we're running in an interactive graphical session.
static bool GetBundleDirectory(const FileSpec &file, FileSpec &bundle_directory)
If you have an executable that is in a bundle and want to get back to the bundle directory from the p...
void SetGroupID(uint32_t gid)
Definition: ProcessInfo.h:59
bool ProcessIDIsValid() const
Definition: ProcessInfo.h:71
const char * GetName() const
Definition: ProcessInfo.cpp:43
lldb::pid_t GetProcessID() const
Definition: ProcessInfo.h:67
void SetProcessID(lldb::pid_t pid)
Definition: ProcessInfo.h:69
FileSpec & GetExecutableFile()
Definition: ProcessInfo.h:42
uint32_t GetUserID() const
Definition: ProcessInfo.h:49
Environment & GetEnvironment()
Definition: ProcessInfo.h:87
void SetUserID(uint32_t uid)
Definition: ProcessInfo.h:57
ArchSpec & GetArchitecture()
Definition: ProcessInfo.h:61
bool Matches(const ProcessInstanceInfo &proc_info) const
ProcessInstanceInfo & GetProcessInfo()
Definition: ProcessInfo.h:183
bool ProcessIDsMatch(const ProcessInstanceInfo &proc_info) const
Return true iff the process ID and parent process IDs in this object match the ones in proc_info.
bool UserIDsMatch(const ProcessInstanceInfo &proc_info) const
Return true iff the (both effective and real) user and group IDs in this object match the ones in pro...
void SetEffectiveGroupID(uint32_t gid)
Definition: ProcessInfo.h:143
void SetParentProcessID(lldb::pid_t pid)
Definition: ProcessInfo.h:147
void SetEffectiveUserID(uint32_t uid)
Definition: ProcessInfo.h:141
const FileSpec & GetShell() const
const FileAction * GetFileActionAtIndex(size_t idx) const
const FileAction * GetFileActionForFD(int fd) const
void SetWorkingDirectory(const FileSpec &working_dir)
const FileSpec & GetWorkingDirectory() const
An error handling class.
Definition: Status.h:44
bool Fail() const
Test for error condition.
Definition: Status.cpp:181
bool Success() const
Test for success condition.
Definition: Status.cpp:287
const char * GetData() const
Definition: StreamString.h:43
llvm::StringRef GetString() const
void Format(const char *format, Args &&... args)
Definition: Stream.h:309
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:107
size_t PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition: Stream.cpp:63
static ObjectSP ParseJSON(const std::string &json_text)
static llvm::Expected< HostThread > LaunchThread(llvm::StringRef name, std::function< lldb::thread_result_t()> thread_function, size_t min_stack_byte_size=0)
#define LLDB_INVALID_CPUTYPE
Definition: lldb-defines.h:95
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:126
#define UINT32_MAX
Definition: lldb-defines.h:19
#define LLDB_INVALID_PROCESS_ID
Definition: lldb-defines.h:81
lldb::ByteOrder InlHostByteOrder()
Definition: Endian.h:25
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition: Log.h:309
bool NameMatches(llvm::StringRef name, NameMatch match_type, llvm::StringRef match)
Definition: NameMatches.cpp:15
std::vector< ProcessInstanceInfo > ProcessInstanceInfoList
Definition: Host.h:32
Definition: SBAddress.h:15
void * thread_result_t
Definition: lldb-types.h:62
ConnectionStatus
Connection Status Types.
@ eConnectionStatusSuccess
Success.
uint64_t offset_t
Definition: lldb-types.h:83
@ eErrorTypeGeneric
Generic errors that can be any value.
@ eErrorTypePOSIX
POSIX error codes.
uint64_t pid_t
Definition: lldb-types.h:81
#define PATH_MAX