1635 lines · plain
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-exception6//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_OSX16#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 the22// 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#endif38 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_ASLR91#define _POSIX_SPAWN_DISABLE_ASLR 0x010092#endif93 94extern "C" {95int __pthread_chdir(const char *path);96int __pthread_fchdir(int fildes);97}98 99using namespace lldb;100using namespace lldb_private;101 102static os_log_t g_os_log;103static std::once_flag g_os_log_once;104 105void Host::SystemLog(Severity severity, llvm::StringRef message) {106 if (__builtin_available(macos 10.12, iOS 10, tvOS 10, watchOS 3, *)) {107 std::call_once(g_os_log_once, []() {108 g_os_log = os_log_create("com.apple.dt.lldb", "lldb");109 });110 switch (severity) {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#endif138 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#endif159 return false;160}161 162#if TARGET_OS_OSX163 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 Status206LaunchInNewTerminalWithAppleScript(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 on260 // the host to what is supplied in the launch_info. Any items that aren't in261 // the host environment need to be sent to darwin-debug. If we send all262 // environment entries, we might blow the max command line length, so we only263 // 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 else280 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_url310 // so we can grab the pid from the inferior. We have to do this because we311 // 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 get313 // to the process that we wanted to launch. So when our process actually314 // 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_OSX337 338llvm::Error Host::OpenFileInExternalEditor(llvm::StringRef editor,339 const FileSpec &file_spec,340 uint32_t line_no) {341#if !TARGET_OS_OSX342 return llvm::errorCodeToError(343 std::error_code(ENOTSUP, std::system_category()));344#else // !TARGET_OS_OSX345 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 zero371 int16_t fLineNumber;372 int32_t fSelStart;373 int32_t fSelEnd;374 uint32_t reserved1; // must be zero375 uint32_t reserved2; // must be zero376 } BabelAESelInfo;377 378 // We attach this to an 'odoc' event to specify a particular selection.379 BabelAESelInfo file_and_line_info = {380 0, // reserved0381 (int16_t)(line_no - 1), // fLineNumber (zero based line number)382 1, // fSelStart383 1024, // fSelEnd384 0, // reserved1385 0 // reserved2386 };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_OSX453}454 455llvm::Error Host::OpenURL(llvm::StringRef url) {456#if !TARGET_OS_OSX457 return llvm::errorCodeToError(458 std::error_code(ENOTSUP, std::system_category()));459#else // !TARGET_OS_OSX460 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_OSX485}486 487bool Host::IsInteractiveGraphicSession() {488#if !TARGET_OS_OSX489 return false;490#else491 auditinfo_addr_t info;492 getaudit_addr(&info, sizeof(info));493 return info.ai_flags & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS;494#endif495}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 safe502 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#endif528 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#endif534 535 case CPU_TYPE_ARM: {536 // Note that we fetched the cpu type from the PROCESS but we can't get a537 // cpusubtype of the538 // 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 in552 // CPU_SUBTYPE_ARM64 numbering553 // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value554 // 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 least584 // one byte585 // 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);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 triple_arch == llvm::Triple::aarch64);600 601 const char *cstr = data.GetCStr(&offset);602 if (cstr) {603 process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);604 605 if (match_info_ptr == NULL ||606 NameMatches(607 process_info.GetExecutableFile().GetFilename().GetCString(),608 match_info_ptr->GetNameMatchType(),609 match_info_ptr->GetProcessInfo().GetName())) {610 // Skip NULLs611 while (true) {612 const uint8_t *p = data.PeekData(offset, 1);613 if ((p == NULL) || (*p != '\0'))614 break;615 ++offset;616 }617 // Now extract all arguments618 Args &proc_args = process_info.GetArguments();619 for (int i = 0; i < static_cast<int>(argc); ++i) {620 cstr = data.GetCStr(&offset);621 if (cstr)622 proc_args.AppendArgument(llvm::StringRef(cstr));623 }624 625 Environment &proc_env = process_info.GetEnvironment();626 bool is_simulator = false;627 llvm::StringRef env_var;628 while (!(env_var = data.GetCStr(&offset)).empty()) {629 if (check_for_ios_simulator &&630 env_var.starts_with("SIMULATOR_UDID="))631 is_simulator = true;632 proc_env.insert(env_var);633 }634 llvm::Triple &triple = process_info.GetArchitecture().GetTriple();635 if (is_simulator) {636 triple.setOS(llvm::Triple::IOS);637 triple.setEnvironment(llvm::Triple::Simulator);638 } else {639 triple.setOS(llvm::Triple::MacOSX);640 }641 return true;642 }643 }644 }645 }646 return false;647}648 649static bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) {650 if (process_info.ProcessIDIsValid()) {651 int mib[4];652 mib[0] = CTL_KERN;653 mib[1] = KERN_PROC;654 mib[2] = KERN_PROC_PID;655 mib[3] = process_info.GetProcessID();656 struct kinfo_proc proc_kinfo;657 size_t proc_kinfo_size = sizeof(struct kinfo_proc);658 659 if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {660 if (proc_kinfo_size > 0) {661 process_info.SetParentProcessID(proc_kinfo.kp_eproc.e_ppid);662 process_info.SetUserID(proc_kinfo.kp_eproc.e_pcred.p_ruid);663 process_info.SetGroupID(proc_kinfo.kp_eproc.e_pcred.p_rgid);664 process_info.SetEffectiveUserID(proc_kinfo.kp_eproc.e_ucred.cr_uid);665 if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)666 process_info.SetEffectiveGroupID(667 proc_kinfo.kp_eproc.e_ucred.cr_groups[0]);668 else669 process_info.SetEffectiveGroupID(UINT32_MAX);670 return true;671 }672 }673 }674 process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);675 process_info.SetUserID(UINT32_MAX);676 process_info.SetGroupID(UINT32_MAX);677 process_info.SetEffectiveUserID(UINT32_MAX);678 process_info.SetEffectiveGroupID(UINT32_MAX);679 return false;680}681 682uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,683 ProcessInstanceInfoList &process_infos) {684 std::vector<struct kinfo_proc> kinfos;685 686 int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};687 688 size_t pid_data_size = 0;689 if (::sysctl(mib, 3, nullptr, &pid_data_size, nullptr, 0) != 0)690 return 0;691 692 // Add a few extra in case a few more show up693 const size_t estimated_pid_count =694 (pid_data_size / sizeof(struct kinfo_proc)) + 10;695 696 kinfos.resize(estimated_pid_count);697 pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);698 699 if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, nullptr, 0) != 0)700 return 0;701 702 const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));703 704 bool all_users = match_info.GetMatchAllUsers();705 const lldb::pid_t our_pid = getpid();706 const uid_t our_uid = getuid();707 for (size_t i = 0; i < actual_pid_count; i++) {708 const struct kinfo_proc &kinfo = kinfos[i];709 710 bool kinfo_user_matches = false;711 if (all_users)712 kinfo_user_matches = true;713 else714 kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid;715 716 // Special case, if lldb is being run as root we can attach to anything.717 if (our_uid == 0)718 kinfo_user_matches = true;719 720 if (!kinfo_user_matches || // Make sure the user is acceptable721 static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) ==722 our_pid || // Skip this process723 kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero)724 kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains...725 kinfo.kp_proc.p_flag & P_TRACED || // Being debugged?726 kinfo.kp_proc.p_flag & P_WEXIT)727 continue;728 729 ProcessInstanceInfo process_info;730 process_info.SetProcessID(kinfo.kp_proc.p_pid);731 process_info.SetParentProcessID(kinfo.kp_eproc.e_ppid);732 process_info.SetUserID(kinfo.kp_eproc.e_pcred.p_ruid);733 process_info.SetGroupID(kinfo.kp_eproc.e_pcred.p_rgid);734 process_info.SetEffectiveUserID(kinfo.kp_eproc.e_ucred.cr_uid);735 if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0)736 process_info.SetEffectiveGroupID(kinfo.kp_eproc.e_ucred.cr_groups[0]);737 else738 process_info.SetEffectiveGroupID(UINT32_MAX);739 740 // Make sure our info matches before we go fetch the name and cpu type741 if (!match_info.UserIDsMatch(process_info) ||742 !match_info.ProcessIDsMatch(process_info))743 continue;744 745 // Get CPU type first so we can know to look for iOS simulator if we have746 // a compatible type.747 if (GetMacOSXProcessCPUType(process_info)) {748 if (GetMacOSXProcessArgs(&match_info, process_info)) {749 if (match_info.Matches(process_info))750 process_infos.push_back(process_info);751 }752 }753 }754 return process_infos.size();755}756 757bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {758 process_info.SetProcessID(pid);759 bool success = false;760 761 // Get CPU type first so we can know to look for iOS simulator is we have x86762 // or x86_64763 if (GetMacOSXProcessCPUType(process_info))764 success = true;765 766 if (GetMacOSXProcessArgs(NULL, process_info))767 success = true;768 769 if (GetMacOSXProcessUserAndGroup(process_info))770 success = true;771 772 if (success)773 return true;774 775 process_info.Clear();776 return false;777}778 779#if TARGET_OS_OSX780static void PackageXPCArguments(xpc_object_t message, const char *prefix,781 const Args &args) {782 size_t count = args.GetArgumentCount();783 char buf[50]; // long enough for 'argXXX'784 memset(buf, 0, sizeof(buf));785 snprintf(buf, sizeof(buf), "%sCount", prefix);786 xpc_dictionary_set_int64(message, buf, count);787 for (size_t i = 0; i < count; i++) {788 memset(buf, 0, sizeof(buf));789 snprintf(buf, sizeof(buf), "%s%zi", prefix, i);790 xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i));791 }792}793 794static void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix,795 const Environment &env) {796 xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(),797 env.size());798 size_t i = 0;799 for (const auto &KV : env) {800 xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(),801 Environment::compose(KV).c_str());802 }803}804 805/*806 A valid authorizationRef means that807 - there is the LaunchUsingXPCRightName rights in the /etc/authorization808 - we have successfully copied the rights to be send over the XPC wire809 Once obtained, it will be valid for as long as the process lives.810 */811static AuthorizationRef authorizationRef = NULL;812static Status getXPCAuthorization(ProcessLaunchInfo &launch_info) {813 Status error;814 Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));815 816 if ((launch_info.GetUserID() == 0) && !authorizationRef) {817 OSStatus createStatus =818 AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,819 kAuthorizationFlagDefaults, &authorizationRef);820 if (createStatus != errAuthorizationSuccess) {821 error = Status(1, eErrorTypeGeneric);822 error = Status::FromErrorString("Can't create authorizationRef.");823 LLDB_LOG(log, "error: {0}", error);824 return error;825 }826 827 OSStatus rightsStatus =828 AuthorizationRightGet(LaunchUsingXPCRightName, NULL);829 if (rightsStatus != errAuthorizationSuccess) {830 // No rights in the security database, Create it with the right prompt.831 CFStringRef prompt =832 CFSTR("Xcode is trying to take control of a root process.");833 CFStringRef keys[] = {CFSTR("en")};834 CFTypeRef values[] = {prompt};835 CFDictionaryRef promptDict = CFDictionaryCreate(836 kCFAllocatorDefault, (const void **)keys, (const void **)values, 1,837 &kCFCopyStringDictionaryKeyCallBacks,838 &kCFTypeDictionaryValueCallBacks);839 840 CFStringRef keys1[] = {CFSTR("class"), CFSTR("group"), CFSTR("comment"),841 CFSTR("default-prompt"), CFSTR("shared")};842 CFTypeRef values1[] = {CFSTR("user"), CFSTR("admin"),843 CFSTR(LaunchUsingXPCRightName), promptDict,844 kCFBooleanFalse};845 CFDictionaryRef dict = CFDictionaryCreate(846 kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5,847 &kCFCopyStringDictionaryKeyCallBacks,848 &kCFTypeDictionaryValueCallBacks);849 rightsStatus = AuthorizationRightSet(850 authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL);851 CFRelease(promptDict);852 CFRelease(dict);853 }854 855 OSStatus copyRightStatus = errAuthorizationDenied;856 if (rightsStatus == errAuthorizationSuccess) {857 AuthorizationItem item1 = {LaunchUsingXPCRightName, 0, NULL, 0};858 AuthorizationItem items[] = {item1};859 AuthorizationRights requestedRights = {1, items};860 AuthorizationFlags authorizationFlags =861 kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;862 copyRightStatus = AuthorizationCopyRights(863 authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment,864 authorizationFlags, NULL);865 }866 867 if (copyRightStatus != errAuthorizationSuccess) {868 // Eventually when the commandline supports running as root and the user869 // is not870 // logged in to the current audit session, we will need the trick in gdb871 // where872 // we ask the user to type in the root passwd in the terminal.873 error = Status(2, eErrorTypeGeneric);874 error = Status::FromErrorStringWithFormat(875 "Launching as root needs root authorization.");876 LLDB_LOG(log, "error: {0}", error);877 878 if (authorizationRef) {879 AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);880 authorizationRef = NULL;881 }882 }883 }884 885 return error;886}887#endif888 889static short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) {890 short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;891 892 if (launch_info.GetFlags().Test(eLaunchFlagExec))893 flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag894 895 if (launch_info.GetFlags().Test(eLaunchFlagDebug))896 flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag897 898 if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))899 flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag900 901 if (launch_info.GetLaunchInSeparateProcessGroup())902 flags |= POSIX_SPAWN_SETPGROUP;903 904#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT905#if defined(__x86_64__) || defined(__i386__)906 static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate;907 if (g_use_close_on_exec_flag == eLazyBoolCalculate) {908 g_use_close_on_exec_flag = eLazyBoolNo;909 910 llvm::VersionTuple version = HostInfo::GetOSVersion();911 if (version > llvm::VersionTuple(10, 7)) {912 // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or913 // earlier914 g_use_close_on_exec_flag = eLazyBoolYes;915 }916 }917#else918 static LazyBool g_use_close_on_exec_flag = eLazyBoolYes;919#endif // defined(__x86_64__) || defined(__i386__)920 // Close all files exception those with file actions if this is supported.921 if (g_use_close_on_exec_flag == eLazyBoolYes)922 flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;923#endif // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT924 return flags;925}926 927static void finalize_xpc(void *xpc_object) {928 xpc_release((xpc_object_t)xpc_object);929}930 931static Status LaunchProcessXPC(const char *exe_path,932 ProcessLaunchInfo &launch_info,933 lldb::pid_t &pid) {934#if TARGET_OS_OSX935 Status error = getXPCAuthorization(launch_info);936 if (error.Fail())937 return error;938 939 Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));940 941 uid_t requested_uid = launch_info.GetUserID();942 const char *xpc_service = nil;943 bool send_auth = false;944 AuthorizationExternalForm extForm;945 if (requested_uid == 0) {946 if (AuthorizationMakeExternalForm(authorizationRef, &extForm) ==947 errAuthorizationSuccess) {948 send_auth = true;949 } else {950 error = Status(3, eErrorTypeGeneric);951 error = Status::FromErrorStringWithFormat(952 "Launching root via XPC needs to "953 "externalize authorization reference.");954 LLDB_LOG(log, "error: {0}", error);955 return error;956 }957 xpc_service = LaunchUsingXPCRightName;958 } else {959 error = Status(4, eErrorTypeGeneric);960 error = Status::FromErrorStringWithFormat(961 "Launching via XPC is only currently available for root.");962 LLDB_LOG(log, "error: {0}", error);963 return error;964 }965 966 xpc_connection_t conn = xpc_connection_create(xpc_service, NULL);967 968 xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {969 xpc_type_t type = xpc_get_type(event);970 971 if (type == XPC_TYPE_ERROR) {972 if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {973 // The service has either canceled itself, crashed, or been terminated.974 // The XPC connection is still valid and sending a message to it will975 // re-launch the service.976 // If the service is state-full, this is the time to initialize the new977 // service.978 return;979 } else if (event == XPC_ERROR_CONNECTION_INVALID) {980 // The service is invalid. Either the service name supplied to981 // xpc_connection_create() is incorrect982 // or we (this process) have canceled the service; we can do any cleanup983 // of application state at this point.984 // printf("Service disconnected");985 return;986 } else {987 // printf("Unexpected error from service: %s",988 // xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));989 }990 991 } else {992 // printf("Received unexpected event in handler");993 }994 });995 996 xpc_connection_set_finalizer_f(conn, finalize_xpc);997 xpc_connection_resume(conn);998 xpc_object_t message = xpc_dictionary_create(nil, nil, 0);999 1000 if (send_auth) {1001 xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes,1002 sizeof(AuthorizationExternalForm));1003 }1004 1005 PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey,1006 launch_info.GetArguments());1007 PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey,1008 launch_info.GetEnvironment());1009 1010 // Posix spawn stuff.1011 xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey,1012 launch_info.GetArchitecture().GetMachOCPUType());1013 xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey,1014 GetPosixspawnFlags(launch_info));1015 const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO);1016 if (file_action && !file_action->GetPath().empty()) {1017 xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey,1018 file_action->GetPath().str().c_str());1019 }1020 file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);1021 if (file_action && !file_action->GetPath().empty()) {1022 xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey,1023 file_action->GetPath().str().c_str());1024 }1025 file_action = launch_info.GetFileActionForFD(STDERR_FILENO);1026 if (file_action && !file_action->GetPath().empty()) {1027 xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey,1028 file_action->GetPath().str().c_str());1029 }1030 1031 xpc_object_t reply =1032 xpc_connection_send_message_with_reply_sync(conn, message);1033 xpc_type_t returnType = xpc_get_type(reply);1034 if (returnType == XPC_TYPE_DICTIONARY) {1035 pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey);1036 if (pid == 0) {1037 int errorType =1038 xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey);1039 int errorCode =1040 xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey);1041 1042 error = Status(errorCode, eErrorTypeGeneric);1043 error = Status::FromErrorStringWithFormat(1044 "Problems with launching via XPC. Error type : %i, code : %i",1045 errorType, errorCode);1046 LLDB_LOG(log, "error: {0}", error);1047 1048 if (authorizationRef) {1049 AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);1050 authorizationRef = NULL;1051 }1052 }1053 } else if (returnType == XPC_TYPE_ERROR) {1054 error = Status(5, eErrorTypeGeneric);1055 error = Status::FromErrorStringWithFormat(1056 "Problems with launching via XPC. XPC error : %s",1057 xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION));1058 LLDB_LOG(log, "error: {0}", error);1059 }1060 1061 return error;1062#else1063 Status error;1064 return error;1065#endif1066}1067 1068static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info,1069 Log *log, Status &error) {1070 if (info == NULL)1071 return false;1072 1073 posix_spawn_file_actions_t *file_actions =1074 static_cast<posix_spawn_file_actions_t *>(_file_actions);1075 1076 switch (info->GetAction()) {1077 case FileAction::eFileActionNone:1078 error.Clear();1079 break;1080 1081 case FileAction::eFileActionClose:1082 if (info->GetFD() == -1)1083 error = Status::FromErrorString(1084 "invalid fd for posix_spawn_file_actions_addclose(...)");1085 else {1086 error = Status(1087 ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()),1088 eErrorTypePOSIX);1089 if (error.Fail())1090 LLDB_LOG(log,1091 "error: {0}, posix_spawn_file_actions_addclose "1092 "(action={1}, fd={2})",1093 error, file_actions, info->GetFD());1094 }1095 break;1096 1097 case FileAction::eFileActionDuplicate:1098 if (info->GetFD() == -1)1099 error = Status::FromErrorString(1100 "invalid fd for posix_spawn_file_actions_adddup2(...)");1101 else if (info->GetActionArgument() == -1)1102 error = Status::FromErrorString(1103 "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)");1104 else if (info->GetFD() != info->GetActionArgument()) {1105 error =1106 Status(::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(),1107 info->GetActionArgument()),1108 eErrorTypePOSIX);1109 if (error.Fail())1110 LLDB_LOG(log,1111 "error: {0}, posix_spawn_file_actions_adddup2 "1112 "(action={1}, fd={2}, dup_fd={3})",1113 error, file_actions, info->GetFD(), info->GetActionArgument());1114 } else {1115 error =1116 Status(::posix_spawn_file_actions_addinherit_np(file_actions, info->GetFD()),1117 eErrorTypePOSIX);1118 if (error.Fail())1119 LLDB_LOG(log,1120 "error: {0}, posix_spawn_file_actions_addinherit_np "1121 "(action={1}, fd={2})",1122 error, file_actions, info->GetFD());1123 }1124 break;1125 1126 case FileAction::eFileActionOpen:1127 if (info->GetFD() == -1)1128 error = Status::FromErrorString(1129 "invalid fd in posix_spawn_file_actions_addopen(...)");1130 else {1131 int oflag = info->GetActionArgument();1132 1133 mode_t mode = 0;1134 1135 if (oflag & O_CREAT)1136 mode = 0640;1137 1138 error = Status(::posix_spawn_file_actions_addopen(1139 file_actions, info->GetFD(),1140 info->GetPath().str().c_str(), oflag, mode),1141 eErrorTypePOSIX);1142 if (error.Fail())1143 LLDB_LOG(log,1144 "error: {0}, posix_spawn_file_actions_addopen (action={1}, "1145 "fd={2}, path='{3}', oflag={4}, mode={5})",1146 error, file_actions, info->GetFD(), info->GetPath(), oflag,1147 mode);1148 }1149 break;1150 }1151 return error.Success();1152}1153 1154static Status LaunchProcessPosixSpawn(const char *exe_path,1155 const ProcessLaunchInfo &launch_info,1156 lldb::pid_t &pid) {1157 Status error;1158 Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));1159 1160 posix_spawnattr_t attr;1161 error = Status(::posix_spawnattr_init(&attr), eErrorTypePOSIX);1162 1163 if (error.Fail()) {1164 LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error);1165 return error;1166 }1167 1168 // Make sure we clean up the posix spawn attributes before exiting this scope.1169 auto cleanup_attr =1170 llvm::make_scope_exit([&]() { posix_spawnattr_destroy(&attr); });1171 1172 sigset_t no_signals;1173 sigset_t all_signals;1174 sigemptyset(&no_signals);1175 sigfillset(&all_signals);1176 ::posix_spawnattr_setsigmask(&attr, &no_signals);1177 ::posix_spawnattr_setsigdefault(&attr, &all_signals);1178 1179 short flags = GetPosixspawnFlags(launch_info);1180 1181 error = Status(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX);1182 if (error.Fail()) {1183 LLDB_LOG(log,1184 "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )",1185 error, flags);1186 return error;1187 }1188 1189 bool is_graphical = true;1190 1191#if TARGET_OS_OSX1192 SecuritySessionId session_id;1193 SessionAttributeBits session_attributes;1194 OSStatus status =1195 SessionGetInfo(callerSecuritySession, &session_id, &session_attributes);1196 if (status == errSessionSuccess)1197 is_graphical = session_attributes & sessionHasGraphicAccess;1198#endif1199 1200 // When lldb is ran through a graphical session, make the debuggee process1201 // responsible for its own TCC permissions instead of inheriting them from1202 // its parent.1203 if (is_graphical && launch_info.GetFlags().Test(eLaunchFlagDebug) &&1204 !launch_info.GetFlags().Test(eLaunchFlagInheritTCCFromParent)) {1205 error = Status(setup_posix_spawn_responsible_flag(&attr), eErrorTypePOSIX);1206 if (error.Fail()) {1207 LLDB_LOG(log, "error: {0}, setup_posix_spawn_responsible_flag(&attr)",1208 error);1209 return error;1210 }1211 }1212 1213 if (launch_info.GetFlags().Test(eLaunchFlagMemoryTagging)) {1214 // The following function configures the spawn attributes to launch the1215 // process with memory tagging explicitly enabled. We look it up1216 // dynamically since it is only available on newer OS. Does nothing on1217 // hardware which does not support MTE.1218 //1219 // int posix_spawnattr_set_use_sec_transition_shims_np(1220 // posix_spawnattr_t *attr, uint32_t flags);1221 //1222 using posix_spawnattr_set_use_sec_transition_shims_np_t =1223 int (*)(posix_spawnattr_t *attr, uint32_t flags);1224 auto posix_spawnattr_enable_memory_tagging_fn =1225 (posix_spawnattr_set_use_sec_transition_shims_np_t)dlsym(1226 RTLD_DEFAULT, "posix_spawnattr_set_use_sec_transition_shims_np");1227 if (posix_spawnattr_enable_memory_tagging_fn) {1228 error = Status(posix_spawnattr_enable_memory_tagging_fn(&attr, 0),1229 eErrorTypePOSIX);1230 if (error.Fail()) {1231 LLDB_LOG(log,1232 "error: {0}, "1233 "posix_spawnattr_set_use_sec_transition_shims_np(&attr, 0)",1234 error);1235 return error;1236 }1237 } else {1238 LLDB_LOG(log,1239 "error: posix_spawnattr_set_use_sec_transition_shims_np not "1240 "available",1241 error);1242 }1243 }1244 1245 // Don't set the binpref if a shell was provided. After all, that's only1246 // going to affect what version of the shell is launched, not what fork of1247 // the binary is launched. We insert "arch --arch <ARCH> as part of the1248 // shell invocation to do that job on OSX.1249 if (launch_info.GetShell() == FileSpec()) {1250 const ArchSpec &arch_spec = launch_info.GetArchitecture();1251 cpu_type_t cpu_type = arch_spec.GetMachOCPUType();1252 cpu_type_t cpu_subtype = arch_spec.GetMachOCPUSubType();1253 const bool set_cpu_type =1254 cpu_type != 0 && cpu_type != static_cast<cpu_type_t>(UINT32_MAX) &&1255 cpu_type != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE);1256 const bool set_cpu_subtype =1257 cpu_subtype != 0 &&1258 cpu_subtype != static_cast<cpu_subtype_t>(UINT32_MAX) &&1259 cpu_subtype != CPU_SUBTYPE_X86_64_H;1260 if (set_cpu_type) {1261 size_t ocount = 0;1262 typedef int (*posix_spawnattr_setarchpref_np_t)(1263 posix_spawnattr_t *, size_t, cpu_type_t *, cpu_subtype_t *, size_t *);1264 posix_spawnattr_setarchpref_np_t posix_spawnattr_setarchpref_np_fn =1265 (posix_spawnattr_setarchpref_np_t)dlsym(1266 RTLD_DEFAULT, "posix_spawnattr_setarchpref_np");1267 if (set_cpu_subtype && posix_spawnattr_setarchpref_np_fn) {1268 error = Status((*posix_spawnattr_setarchpref_np_fn)(1269 &attr, 1, &cpu_type, &cpu_subtype, &ocount),1270 eErrorTypePOSIX);1271 if (error.Fail())1272 LLDB_LOG(log,1273 "error: {0}, ::posix_spawnattr_setarchpref_np ( &attr, 1, "1274 "cpu_type = {1:x}, cpu_subtype = {1:x}, count => {2} )",1275 error, cpu_type, cpu_subtype, ocount);1276 1277 if (error.Fail() || ocount != 1)1278 return error;1279 } else {1280 error = Status(1281 ::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount),1282 eErrorTypePOSIX);1283 if (error.Fail())1284 LLDB_LOG(log,1285 "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, "1286 "cpu_type = {1:x}, count => {2} )",1287 error, cpu_type, ocount);1288 if (error.Fail() || ocount != 1)1289 return error;1290 }1291 }1292 }1293 1294 const char *tmp_argv[2];1295 char *const *argv = const_cast<char *const *>(1296 launch_info.GetArguments().GetConstArgumentVector());1297 Environment::Envp envp = launch_info.GetEnvironment().getEnvp();1298 if (argv == NULL) {1299 // posix_spawn gets very unhappy if it doesn't have at least the program1300 // name in argv[0]. One of the side affects I have noticed is the1301 // environment1302 // variables don't make it into the child process if "argv == NULL"!!!1303 tmp_argv[0] = exe_path;1304 tmp_argv[1] = NULL;1305 argv = const_cast<char *const *>(tmp_argv);1306 }1307 1308 FileSpec working_dir{launch_info.GetWorkingDirectory()};1309 if (working_dir) {1310 // Set the working directory on this thread only1311 std::string working_dir_path = working_dir.GetPath();1312 if (__pthread_chdir(working_dir_path.c_str()) < 0) {1313 if (errno == ENOENT) {1314 error = Status::FromErrorStringWithFormat(1315 "No such file or directory: %s", working_dir_path.c_str());1316 } else if (errno == ENOTDIR) {1317 error = Status::FromErrorStringWithFormat(1318 "Path doesn't name a directory: %s", working_dir_path.c_str());1319 } else {1320 error =1321 Status::FromErrorStringWithFormat("An unknown error occurred when "1322 "changing directory for process "1323 "execution.");1324 }1325 return error;1326 }1327 }1328 1329 ::pid_t result_pid = LLDB_INVALID_PROCESS_ID;1330 const size_t num_file_actions = launch_info.GetNumFileActions();1331 if (num_file_actions > 0) {1332 posix_spawn_file_actions_t file_actions;1333 error =1334 Status(::posix_spawn_file_actions_init(&file_actions), eErrorTypePOSIX);1335 if (error.Fail()) {1336 LLDB_LOG(log,1337 "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )",1338 error);1339 return error;1340 }1341 1342 // Make sure we clean up the posix file actions before exiting this scope.1343 auto cleanup_fileact = llvm::make_scope_exit(1344 [&]() { posix_spawn_file_actions_destroy(&file_actions); });1345 1346 for (size_t i = 0; i < num_file_actions; ++i) {1347 const FileAction *launch_file_action =1348 launch_info.GetFileActionAtIndex(i);1349 if (launch_file_action) {1350 if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log,1351 error))1352 return error;1353 }1354 }1355 1356 error = Status(1357 ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp),1358 eErrorTypePOSIX);1359 1360 if (error.Fail()) {1361 LLDB_LOG(log,1362 "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', "1363 "file_actions = {3}, "1364 "attr = {4}, argv = {5}, envp = {6} )",1365 error, result_pid, exe_path, &file_actions, &attr, argv,1366 envp.get());1367 if (log) {1368 for (int ii = 0; argv[ii]; ++ii)1369 LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);1370 }1371 }1372 1373 } else {1374 error =1375 Status(::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp),1376 eErrorTypePOSIX);1377 1378 if (error.Fail()) {1379 LLDB_LOG(log,1380 "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', "1381 "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )",1382 error, result_pid, exe_path, &attr, argv, envp.get());1383 if (log) {1384 for (int ii = 0; argv[ii]; ++ii)1385 LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);1386 }1387 }1388 }1389 pid = result_pid;1390 1391 if (working_dir) {1392 // No more thread specific current working directory1393 __pthread_fchdir(-1);1394 }1395 1396 return error;1397}1398 1399static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) {1400 bool result = false;1401 1402#if TARGET_OS_OSX1403 bool launchingAsRoot = launch_info.GetUserID() == 0;1404 bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0;1405 1406 if (launchingAsRoot && !currentUserIsRoot) {1407 // If current user is already root, we don't need XPC's help.1408 result = true;1409 }1410#endif1411 1412 return result;1413}1414 1415Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) {1416 Status error;1417 1418 FileSystem &fs = FileSystem::Instance();1419 FileSpec exe_spec(launch_info.GetExecutableFile());1420 1421 if (!fs.Exists(exe_spec))1422 FileSystem::Instance().Resolve(exe_spec);1423 1424 if (!fs.Exists(exe_spec))1425 FileSystem::Instance().ResolveExecutableLocation(exe_spec);1426 1427 if (!fs.Exists(exe_spec)) {1428 error = Status::FromErrorStringWithFormatv(1429 "executable doesn't exist: '{0}'", exe_spec);1430 return error;1431 }1432 1433 if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) {1434#if TARGET_OS_OSX1435 return LaunchInNewTerminalWithAppleScript(exe_spec.GetPath().c_str(),1436 launch_info);1437#else1438 error =1439 Status::FromErrorString("launching a process in a new terminal is not "1440 "supported on iOS devices");1441 return error;1442#endif1443 }1444 1445 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;1446 1447 auto exe_path = exe_spec.GetPath();1448 1449 if (ShouldLaunchUsingXPC(launch_info))1450 error = LaunchProcessXPC(exe_path.c_str(), launch_info, pid);1451 else1452 error = LaunchProcessPosixSpawn(exe_path.c_str(), launch_info, pid);1453 1454 if (pid != LLDB_INVALID_PROCESS_ID) {1455 // If all went well, then set the process ID into the launch info1456 launch_info.SetProcessID(pid);1457 1458 // Make sure we reap any processes we spawn or we will have zombies.1459 bool monitoring = launch_info.MonitorProcess();1460 UNUSED_IF_ASSERT_DISABLED(monitoring);1461 assert(monitoring);1462 } else {1463 // Invalid process ID, something didn't go well1464 if (error.Success())1465 error =1466 Status::FromErrorString("process launch failed for unknown reasons");1467 }1468 return error;1469}1470 1471Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {1472 Status error;1473 if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {1474 FileSpec expand_tool_spec;1475 Environment host_env = Host::GetEnvironment();1476 std::string env_argdumper_path = host_env.lookup("LLDB_ARGDUMPER_PATH");1477 if (!env_argdumper_path.empty()) {1478 expand_tool_spec.SetFile(env_argdumper_path, FileSpec::Style::native);1479 Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));1480 LLDB_LOGF(log,1481 "lldb-argdumper exe path set from environment variable: %s",1482 env_argdumper_path.c_str());1483 }1484 bool argdumper_exists = FileSystem::Instance().Exists(env_argdumper_path);1485 if (!argdumper_exists) {1486 expand_tool_spec = HostInfo::GetSupportExeDir();1487 if (!expand_tool_spec) {1488 error = Status::FromErrorString(1489 "could not get support executable directory for "1490 "lldb-argdumper tool");1491 return error;1492 }1493 expand_tool_spec.AppendPathComponent("lldb-argdumper");1494 if (!FileSystem::Instance().Exists(expand_tool_spec)) {1495 error = Status::FromErrorStringWithFormat(1496 "could not find the lldb-argdumper tool: %s",1497 expand_tool_spec.GetPath().c_str());1498 return error;1499 }1500 }1501 1502 StreamString expand_tool_spec_stream;1503 expand_tool_spec_stream.Printf("\"%s\"",1504 expand_tool_spec.GetPath().c_str());1505 1506 Args expand_command(expand_tool_spec_stream.GetData());1507 expand_command.AppendArguments(launch_info.GetArguments());1508 1509 int status;1510 std::string output;1511 FileSpec cwd(launch_info.GetWorkingDirectory());1512 if (!FileSystem::Instance().Exists(cwd)) {1513 char *wd = getcwd(nullptr, 0);1514 if (wd == nullptr) {1515 error = Status::FromErrorStringWithFormat(1516 "cwd does not exist: Cannot launch with shell argument expansion");1517 return error;1518 } else {1519 FileSpec working_dir(wd);1520 free(wd);1521 launch_info.SetWorkingDirectory(working_dir);1522 }1523 }1524 bool run_in_shell = true;1525 bool hide_stderr = true;1526 Status e =1527 RunShellCommand(expand_command, cwd, &status, nullptr, &output,1528 std::chrono::seconds(10), run_in_shell, hide_stderr);1529 1530 if (e.Fail())1531 return e;1532 1533 if (status != 0) {1534 error = Status::FromErrorStringWithFormat(1535 "lldb-argdumper exited with error %d", status);1536 return error;1537 }1538 1539 auto data_sp = StructuredData::ParseJSON(output);1540 if (!data_sp) {1541 error = Status::FromErrorString("invalid JSON");1542 return error;1543 }1544 1545 auto dict_sp = data_sp->GetAsDictionary();1546 if (!data_sp) {1547 error = Status::FromErrorString("invalid JSON");1548 return error;1549 }1550 1551 auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");1552 if (!args_sp) {1553 error = Status::FromErrorString("invalid JSON");1554 return error;1555 }1556 1557 auto args_array_sp = args_sp->GetAsArray();1558 if (!args_array_sp) {1559 error = Status::FromErrorString("invalid JSON");1560 return error;1561 }1562 1563 launch_info.GetArguments().Clear();1564 1565 for (size_t i = 0; i < args_array_sp->GetSize(); i++) {1566 auto item_sp = args_array_sp->GetItemAtIndex(i);1567 if (!item_sp)1568 continue;1569 auto str_sp = item_sp->GetAsString();1570 if (!str_sp)1571 continue;1572 1573 launch_info.GetArguments().AppendArgument(str_sp->GetValue());1574 }1575 }1576 1577 return error;1578}1579 1580llvm::Expected<HostThread> Host::StartMonitoringChildProcess(1581 const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) {1582 unsigned long mask = DISPATCH_PROC_EXIT;1583 1584 Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));1585 1586 dispatch_source_t source = ::dispatch_source_create(1587 DISPATCH_SOURCE_TYPE_PROC, pid, mask,1588 ::dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));1589 1590 LLDB_LOGF(log,1591 "Host::StartMonitoringChildProcess(callback, pid=%i) source = %p\n",1592 static_cast<int>(pid), static_cast<void *>(source));1593 1594 if (source) {1595 Host::MonitorChildProcessCallback callback_copy = callback;1596 ::dispatch_source_set_cancel_handler(source, ^{1597 dispatch_release(source);1598 });1599 ::dispatch_source_set_event_handler(source, ^{1600 1601 int status = 0;1602 int wait_pid = 0;1603 wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, 0);1604 if (wait_pid >= 0) {1605 int signal = 0;1606 int exit_status = 0;1607 const char *status_cstr = NULL;1608 if (WIFEXITED(status)) {1609 exit_status = WEXITSTATUS(status);1610 status_cstr = "EXITED";1611 } else if (WIFSIGNALED(status)) {1612 signal = WTERMSIG(status);1613 status_cstr = "SIGNALED";1614 exit_status = -1;1615 } else {1616 llvm_unreachable("Unknown status");1617 }1618 1619 LLDB_LOGF(log,1620 "::waitpid (pid = %llu, &status, 0) => pid = %i, status "1621 "= 0x%8.8x (%s), signal = %i, exit_status = %i",1622 pid, wait_pid, status, status_cstr, signal, exit_status);1623 1624 if (callback_copy)1625 callback_copy(pid, signal, exit_status);1626 1627 ::dispatch_source_cancel(source);1628 }1629 });1630 1631 ::dispatch_resume(source);1632 }1633 return HostThread();1634}1635