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

source code of lldb/source/Host/macosx/objcxx/Host.mm