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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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