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