<rdar://problem/3535148>
Added ability to debug root processes on OS X. This uses XPC service that is available on Lion and above only.
git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@151419 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/API/SBTarget.cpp b/source/API/SBTarget.cpp
index 2b6489a..51c0720 100644
--- a/source/API/SBTarget.cpp
+++ b/source/API/SBTarget.cpp
@@ -901,6 +901,14 @@
ProcessAttachInfo attach_info;
attach_info.SetProcessID (pid);
+
+ PlatformSP platform_sp = target_sp->GetPlatform();
+ ProcessInstanceInfo instance_info;
+ if (platform_sp->GetProcessInfo(pid, instance_info))
+ {
+ attach_info.SetUserID(instance_info.GetEffectiveUserID());
+
+ }
error.SetError (process_sp->Attach (attach_info));
// If we are doing synchronous mode, then wait for the
// process to stop!
diff --git a/source/Host/common/Host.cpp b/source/Host/common/Host.cpp
index 5f0362b..e9fdf46 100644
--- a/source/Host/common/Host.cpp
+++ b/source/Host/common/Host.cpp
@@ -1173,7 +1173,31 @@
}
#endif
-#if !defined(__APPLE__)
+uint32_t
+Host::GetUserID ()
+{
+ return getuid();
+}
+
+uint32_t
+Host::GetGroupID ()
+{
+ return getgid();
+}
+
+uint32_t
+Host::GetEffectiveUserID ()
+{
+ return geteuid();
+}
+
+uint32_t
+Host::GetEffectiveGroupID ()
+{
+ return getegid();
+}
+
+#if !defined (__APPLE__)
uint32_t
Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos)
{
diff --git a/source/Host/macosx/Host.mm b/source/Host/macosx/Host.mm
index 9547565..944f05a 100644
--- a/source/Host/macosx/Host.mm
+++ b/source/Host/macosx/Host.mm
@@ -9,6 +9,17 @@
#include "lldb/Host/Host.h"
+#include <AvailabilityMacros.h>
+
+#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+#define BUILDING_ON_SNOW_LEOPARD 1
+#endif
+
+#if !BUILDING_ON_SNOW_LEOPARD
+#include <xpc/xpc.h>
+#include "LauncherXPCService.h"
+#endif
+
#include <asl.h>
#include <crt_externs.h>
#include <execinfo.h>
@@ -53,8 +64,8 @@
#else
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
+#include <Security/Security.h>
#endif
-#include <Foundation/Foundation.h>
#ifndef _POSIX_SPAWN_DISABLE_ASLR
#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
@@ -1219,57 +1230,216 @@
return false;
}
-Error
-Host::LaunchProcess (ProcessLaunchInfo &launch_info)
+static short
+GetPosixspawnFlags (ProcessLaunchInfo &launch_info)
+{
+ short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
+ if (launch_info.GetFlags().Test (eLaunchFlagExec))
+ flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag
+
+ if (launch_info.GetFlags().Test (eLaunchFlagDebug))
+ flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag
+
+ if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR))
+ flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag
+
+ //#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
+ // // Close all files exception those with file actions if this is supported.
+ // flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
+ //#endif
+
+ return flags;
+}
+
+#if !BUILDING_ON_SNOW_LEOPARD
+static void
+PackageXPCArguments (xpc_object_t message, const char *prefix, const Args& args)
+{
+ size_t count = args.GetArgumentCount();
+ char buf[50]; // long enough for 'argXXX'
+ memset(buf, 0, 50);
+ sprintf(buf, "%sCount", prefix);
+ xpc_dictionary_set_int64(message, buf, count);
+ for (int i=0; i<count; i++) {
+ memset(buf, 0, 50);
+ sprintf(buf, "%s%i", prefix, i);
+ xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i));
+ }
+}
+
+static Error
+getXPCAuthorization (ProcessLaunchInfo &launch_info)
{
Error error;
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));
- char exe_path[PATH_MAX];
- PlatformSP host_platform_sp (Platform::GetDefaultPlatform ());
- const ArchSpec &arch_spec = launch_info.GetArchitecture();
-
- FileSpec exe_spec(launch_info.GetExecutableFile());
-
- FileSpec::FileType file_type = exe_spec.GetFileType();
- if (file_type != FileSpec::eFileTypeRegular)
+ if (launch_info.GetUserID() == 0)
{
- lldb::ModuleSP exe_module_sp;
- error = host_platform_sp->ResolveExecutable (exe_spec,
- arch_spec,
- exe_module_sp,
- NULL);
-
- if (error.Fail())
+ CFDictionaryRef dict = NULL;
+ OSStatus osStatus;
+ const char *rightName = "com.apple.lldb.LaunchUsingXPC";
+
+ osStatus = AuthorizationRightGet(rightName, &dict);
+ if (dict) CFRelease(dict);
+ if (osStatus == errAuthorizationSuccess)
+ {
+ // Got the right already.
return error;
-
- if (exe_module_sp)
- exe_spec = exe_module_sp->GetFileSpec();
+ }
+
+ AuthorizationFlags authorizationFlags = kAuthorizationFlagDefaults;
+ AuthorizationRef authorizationRef = NULL;
+ osStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, authorizationFlags, &authorizationRef);
+ if (osStatus != errAuthorizationSuccess)
+ {
+ error.SetError(1, eErrorTypeGeneric);
+ if (log)
+ {
+ error.PutToLog(log.get(), "Can't create authorizationRef.");
+ }
+ else {
+ error.SetErrorString("Can't create authorizationRef.");
+ }
+ return error;
+ }
+
+ CFStringRef prompt = CFSTR("The debugger is debugging a root process. Please authenticate as an administrator.");
+// CFStringRef keys[] = { CFSTR("") };
+// CFTypeRef values[] = { prompt };
+// CFDictionaryRef promptDict = CFDictionaryCreate( kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ int timeout = 1; // Make this 10
+ CFNumberRef timeoutRef = CFNumberCreate(NULL, kCFNumberIntType, &timeout);
+ CFStringRef keys1[] = { CFSTR("class"), CFSTR("group"), CFSTR("comment"), CFSTR("shared"), CFSTR("timeout") };
+ CFTypeRef values1[] = { CFSTR("user"), CFSTR("admin"), CFSTR("com.apple.lldb.LaunchUsingXPC"), kCFBooleanFalse, timeoutRef};
+ dict = CFDictionaryCreate( kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ osStatus = AuthorizationRightSet(authorizationRef, rightName, dict, prompt, NULL, NULL);
+ if (osStatus != errAuthorizationSuccess)
+ {
+ // Eventually when the commandline supports running as root and the user is not
+ // logged in in the current audit session, we will need the trick in gdb where
+ // we ask the user to type in the root passwd in the terminal.
+ error.SetError(2, eErrorTypeGeneric);
+ if (log)
+ {
+ error.PutToLog(log.get(), "Launching as root needs root authorization.");
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("Launching as root needs root authorization.");
+ }
+ }
+ CFRelease(timeoutRef);
+// CFRelease(promptDict);
+ CFRelease(dict);
+ if (authorizationRef) {
+ AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
+ }
}
+
+ return error;
+}
+#endif
+
+static Error
+LaunchProcessXPC (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid)
+{
+#if !BUILDING_ON_SNOW_LEOPARD
+ Error error = getXPCAuthorization(launch_info);
+ if (error.Fail())
+ return error;
- if (exe_spec.Exists())
+ LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));
+
+ uid_t requested_uid = launch_info.GetUserID();
+ const char *xpc_service = nil;
+ if ((requested_uid == UINT32_MAX) || (requested_uid == Host::GetEffectiveUserID()))
{
- exe_spec.GetPath (exe_path, sizeof(exe_path));
+ xpc_service = "com.apple.lldb.launcherXPCService";
+ }
+ else if (requested_uid == 0)
+ {
+ xpc_service = "com.apple.lldb.launcherRootXPCService";
}
else
{
- launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path));
- error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path);
+ error.SetError(2, eErrorTypeGeneric);
+ if (log)
+ {
+ error.PutToLog(log.get(), "Launching via XPC is only currently available for either the login user or root.");
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("Launching via XPC is only currently available for either the login user or root.");
+ }
return error;
}
-
- if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY))
+ xpc_connection_t conn = xpc_connection_create(xpc_service, NULL);
+
+ xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
+ xpc_type_t type = xpc_get_type(event);
+
+ if (type == XPC_TYPE_ERROR) {
+ if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
+ // The service has either canceled itself, crashed, or been terminated.
+ // The XPC connection is still valid and sending a message to it will re-launch the service.
+ // If the service is state-full, this is the time to initialize the new service.
+ return;
+ } else if (event == XPC_ERROR_CONNECTION_INVALID) {
+ // The service is invalid. Either the service name supplied to xpc_connection_create() is incorrect
+ // or we (this process) have canceled the service; we can do any cleanup of appliation state at this point.
+ // printf("Service disconnected");
+ return;
+ } else {
+ // printf("Unexpected error from service: %s", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
+ }
+
+ } else {
+ // printf("Received unexpected event in handler");
+ }
+ });
+
+ xpc_connection_set_finalizer_f (conn, xpc_release);
+ xpc_connection_resume (conn);
+ xpc_object_t message = xpc_dictionary_create (nil, nil, 0);
+
+ PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, launch_info.GetArguments());
+ PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey, launch_info.GetEnvironmentEntries());
+
+ // Posix spawn stuff.
+ xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType());
+ xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, GetPosixspawnFlags(launch_info));
+
+ xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message);
+
+ pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey);
+ if (pid == 0)
{
-#if !defined(__arm__)
- return LaunchInNewTerminalWithAppleScript (exe_path, launch_info);
-#else
- error.SetErrorString ("launching a processs in a new terminal is not supported on iOS devices");
- return error;
-#endif
+ int errorType = xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey);
+ int errorCode = xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey);
+
+ error.SetError(errorCode, eErrorTypeGeneric);
+ if (log)
+ {
+ error.PutToLog(log.get(), "Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode);
+ }
+ else {
+ error.SetErrorStringWithFormat("Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode);
+ }
}
+#endif
- Error local_err; // Errors that don't affect the spawning.
+ return error;
+}
+
+static Error
+LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid)
+{
+ Error error;
+ LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));
+
posix_spawnattr_t attr;
error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX);
@@ -1289,21 +1459,7 @@
::posix_spawnattr_setsigmask(&attr, &no_signals);
::posix_spawnattr_setsigdefault(&attr, &all_signals);
- short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
- if (launch_info.GetFlags().Test (eLaunchFlagExec))
- flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag
-
- if (launch_info.GetFlags().Test (eLaunchFlagDebug))
- flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag
-
- if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR))
- flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag
-
-//#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
-// // Close all files exception those with file actions if this is supported.
-// flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
-//#endif
-
+ short flags = GetPosixspawnFlags(launch_info);
error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX);
if (error.Fail() || log)
error.PutToLog(log.get(), "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags);
@@ -1315,6 +1471,7 @@
// We don't need to do this for ARM, and we really shouldn't now that we
// have multiple CPU subtypes and no posix_spawnattr call that allows us
// to set which CPU subtype to launch...
+ const ArchSpec &arch_spec = launch_info.GetArchitecture();
cpu_type_t cpu = arch_spec.GetMachOCPUType();
if (cpu != 0 &&
cpu != UINT32_MAX &&
@@ -1330,7 +1487,7 @@
}
#endif
- ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
const char *tmp_argv[2];
char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector();
char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector();
@@ -1420,7 +1577,98 @@
// No more thread specific current working directory
__pthread_fchdir (-1);
}
+
+ return error;
+}
+static bool
+ShouldLaunchUsingXPC(const char *exe_path, ProcessLaunchInfo &launch_info)
+{
+ bool result = false;
+
+#if !BUILDING_ON_SNOW_LEOPARD
+ const char *debugserver = "/debugserver";
+ int len = strlen(debugserver);
+ int exe_len = strlen(exe_path);
+ if (exe_len >= len)
+ {
+ const char *part = exe_path + (exe_len - len);
+ if (strcmp(part, debugserver) == 0)
+ {
+ // We are dealing with debugserver.
+ uid_t requested_uid = launch_info.GetUserID();
+ if (requested_uid == 0)
+ {
+ // Launching XPC works for root. It also works for the non-attaching case for current login
+ // but unfortunately, we can't detect it here.
+ result = true;
+ }
+ }
+ }
+#endif
+
+ return result;
+}
+
+Error
+Host::LaunchProcess (ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ char exe_path[PATH_MAX];
+ PlatformSP host_platform_sp (Platform::GetDefaultPlatform ());
+
+ const ArchSpec &arch_spec = launch_info.GetArchitecture();
+
+ FileSpec exe_spec(launch_info.GetExecutableFile());
+
+ FileSpec::FileType file_type = exe_spec.GetFileType();
+ if (file_type != FileSpec::eFileTypeRegular)
+ {
+ lldb::ModuleSP exe_module_sp;
+ error = host_platform_sp->ResolveExecutable (exe_spec,
+ arch_spec,
+ exe_module_sp,
+ NULL);
+
+ if (error.Fail())
+ return error;
+
+ if (exe_module_sp)
+ exe_spec = exe_module_sp->GetFileSpec();
+ }
+
+ if (exe_spec.Exists())
+ {
+ exe_spec.GetPath (exe_path, sizeof(exe_path));
+ }
+ else
+ {
+ launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path));
+ error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path);
+ return error;
+ }
+
+ if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY))
+ {
+#if !defined(__arm__)
+ return LaunchInNewTerminalWithAppleScript (exe_path, launch_info);
+#else
+ error.SetErrorString ("launching a processs in a new terminal is not supported on iOS devices");
+ return error;
+#endif
+ }
+
+ ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ if (ShouldLaunchUsingXPC(exe_path, launch_info))
+ {
+ error = LaunchProcessXPC(exe_path, launch_info, pid);
+ }
+ else
+ {
+ error = LaunchProcessPosixSpawn(exe_path, launch_info, pid);
+ }
+
if (pid != LLDB_INVALID_PROCESS_ID)
{
// If all went well, then set the process ID into the launch info
diff --git a/source/Host/macosx/launcherXPCService/LauncherRootXPCService-Info.plist b/source/Host/macosx/launcherXPCService/LauncherRootXPCService-Info.plist
new file mode 100644
index 0000000..f797f90
--- /dev/null
+++ b/source/Host/macosx/launcherXPCService/LauncherRootXPCService-Info.plist
@@ -0,0 +1,44 @@
+#if RC_XBS && !RC_BUILDIT
+#define AND_APPLE_CODE_SIGNED and ${IS_APPLE_CODE_SIGNED}
+#else
+#define AND_APPLE_CODE_SIGNED
+#endif
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>XPC!</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2012 Apple Inc. All rights reserved.</string>
+ <key>XPCService</key>
+ <dict>
+ <key>_AllowedClients</key>
+ <array>
+ <string> identifier = com.apple.lldb AND_APPLE_CODE_SIGNED</string>
+ <string> identifier = com.apple.dt.Xcode AND_APPLE_CODE_SIGNED</string>
+ </array>
+ <key>_RoleAccount</key>
+ <string>root</string>
+ <key>ServiceType</key>
+ <string>Application</string>
+ </dict>
+</dict>
+</plist>
diff --git a/source/Host/macosx/launcherXPCService/LauncherXPCService-Info.plist b/source/Host/macosx/launcherXPCService/LauncherXPCService-Info.plist
new file mode 100644
index 0000000..31c8c5a
--- /dev/null
+++ b/source/Host/macosx/launcherXPCService/LauncherXPCService-Info.plist
@@ -0,0 +1,44 @@
+#if RC_XBS && !RC_BUILDIT
+#define AND_APPLE_CODE_SIGNED and ${IS_APPLE_CODE_SIGNED}
+#else
+#define AND_APPLE_CODE_SIGNED
+#endif
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>XPC!</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2012 Apple Inc. All rights reserved.</string>
+ <key>XPCService</key>
+ <dict>
+ <key>_AllowedClients</key>
+ <array>
+ <string> identifier = com.apple.lldb AND_APPLE_CODE_SIGNED</string>
+ <string> identifier = com.apple.dt.Xcode AND_APPLE_CODE_SIGNED</string>
+ </array>
+ <key>ServiceType</key>
+ <string>Application</string>
+ <key>JoinExistingSession</key>
+ <true/>
+ </dict>
+</dict>
+</plist>
diff --git a/source/Host/macosx/launcherXPCService/LauncherXPCService.entitlements b/source/Host/macosx/launcherXPCService/LauncherXPCService.entitlements
new file mode 100644
index 0000000..a3088cc
--- /dev/null
+++ b/source/Host/macosx/launcherXPCService/LauncherXPCService.entitlements
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.xpc.role-account</key>
+ <true/>
+</dict>
+</plist>
diff --git a/source/Host/macosx/launcherXPCService/LauncherXPCService.h b/source/Host/macosx/launcherXPCService/LauncherXPCService.h
new file mode 100644
index 0000000..071cea7
--- /dev/null
+++ b/source/Host/macosx/launcherXPCService/LauncherXPCService.h
@@ -0,0 +1,13 @@
+#ifndef LLDB_LauncherXPCService_h
+#define LLDB_LauncherXPCService_h
+
+// These XPC messaging keys are used for communication between Host.mm and the XPC service.
+#define LauncherXPCServiceArgPrefxKey "arg"
+#define LauncherXPCServiceEnvPrefxKey "env"
+#define LauncherXPCServiceCPUTypeKey "cpuType"
+#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags"
+#define LauncherXPCServiceChildPIDKey "childPID"
+#define LauncherXPCServiceErrorTypeKey "errorType"
+#define LauncherXPCServiceCodeTypeKey "errorCode"
+
+#endif
diff --git a/source/Host/macosx/launcherXPCService/LauncherXPCService.mm b/source/Host/macosx/launcherXPCService/LauncherXPCService.mm
new file mode 100644
index 0000000..9ffae6d
--- /dev/null
+++ b/source/Host/macosx/launcherXPCService/LauncherXPCService.mm
@@ -0,0 +1,185 @@
+//
+// LauncherXPCService.m
+// LauncherXPCService
+//
+// Copyright (c) 2012 Apple Inc. All rights reserved.
+//
+#include <AvailabilityMacros.h>
+
+#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+#define BUILDING_ON_SNOW_LEOPARD 1
+#endif
+
+#if !BUILDING_ON_SNOW_LEOPARD
+#include <xpc/xpc.h>
+#include <spawn.h>
+#include <signal.h>
+#include <assert.h>
+#include "LauncherXPCService.h"
+
+// Returns 0 if successful.
+int _setup_posixspawn_attributes_file_actions(xpc_object_t message, posix_spawnattr_t *attr, posix_spawn_file_actions_t *file_actions)
+{
+ *attr = 0;
+
+ int errorCode = posix_spawnattr_init(attr);
+ if (errorCode)
+ return errorCode;
+
+ cpu_type_t cpuType = xpc_dictionary_get_int64(message, LauncherXPCServiceCPUTypeKey);
+ if (cpuType == -2) {
+ cpuType= CPU_TYPE_ANY;
+ }
+ size_t realCount;
+ errorCode = posix_spawnattr_setbinpref_np(attr, 1, &cpuType, &realCount);
+ if (errorCode)
+ return errorCode;
+
+ sigset_t no_signals;
+ sigset_t all_signals;
+ sigemptyset (&no_signals);
+ sigfillset (&all_signals);
+ posix_spawnattr_setsigmask(attr, &no_signals);
+ posix_spawnattr_setsigdefault(attr, &all_signals);
+
+ short flags = xpc_dictionary_get_int64(message, LauncherXPCServicePosixspawnFlagsKey);
+ errorCode = posix_spawnattr_setflags(attr, flags);
+ if (errorCode)
+ return errorCode;
+
+ // Setup any file actions. Here we are emulating what debugserver would do normally in Host.mm since the XPC service meant only for debugserver.
+ errorCode = posix_spawn_file_actions_init(file_actions);
+ if (errorCode)
+ return errorCode;
+ errorCode = posix_spawn_file_actions_addclose(file_actions, STDIN_FILENO);
+ if (errorCode)
+ return errorCode;
+ errorCode = posix_spawn_file_actions_addclose(file_actions, STDOUT_FILENO);
+ if (errorCode)
+ return errorCode;
+ errorCode = posix_spawn_file_actions_addclose(file_actions, STDERR_FILENO);
+
+ return errorCode;
+}
+
+bool extract_args(xpc_object_t message, const char *prefix, const char ***argsOut)
+{
+ char buf[50]; // long enough for 'argXXX'
+ memset(buf, 0, 50);
+ sprintf(buf, "%sCount", prefix);
+ int argsCount = xpc_dictionary_get_int64(message, buf);
+ if (argsCount == 0) {
+ return true;
+ }
+
+ const char **argsp = NULL;
+ argsp = (const char **)malloc((argsCount+1) * sizeof(argsp[0]));
+ if (argsp == NULL) {
+ return false;
+ }
+
+ for (int i=0; i<argsCount; i++) {
+ memset(buf, 0, 50);
+ sprintf(buf, "%s%i", prefix, i);
+ const char *arg = xpc_dictionary_get_string(message, buf);
+ argsp[i] = arg;
+ }
+ argsp[argsCount] = NULL;
+
+ *argsOut = argsp;
+ return true;
+}
+
+// Returns 0 if successful.
+int get_args(xpc_object_t message, const char **path, const char ***argsOut, const char ***envOut)
+{
+ if (!extract_args(message, LauncherXPCServiceArgPrefxKey, argsOut)) {
+ return 1;
+ }
+ *path = (*argsOut)[0];
+
+ if (!extract_args(message, LauncherXPCServiceEnvPrefxKey, envOut)) {
+ return 2;
+ }
+
+ return 0;
+}
+
+static void launcherXPC_peer_event_handler(xpc_connection_t peer, xpc_object_t event)
+{
+ xpc_type_t type = xpc_get_type(event);
+ if (type == XPC_TYPE_ERROR) {
+ if (event == XPC_ERROR_CONNECTION_INVALID) {
+ // The client process on the other end of the connection has either
+ // crashed or cancelled the connection. After receiving this error,
+ // the connection is in an invalid state, and you do not need to
+ // call xpc_connection_cancel(). Just tear down any associated state
+ // here.
+ } else if (event == XPC_ERROR_TERMINATION_IMMINENT) {
+ // Handle per-connection termination cleanup.
+ }
+ } else {
+ assert(type == XPC_TYPE_DICTIONARY);
+ // Handle the message.
+
+ pid_t childPID = 0;
+ posix_spawn_file_actions_t file_actions;
+ posix_spawnattr_t attributes;
+
+ /*
+ Types of error. Error code will be specific to each type.
+ 1 - posixspawn attributes problem
+ 2 - get args/env problem
+ 3 - posixspawn problem
+ */
+ int errorType = 1;
+ int errorCode = _setup_posixspawn_attributes_file_actions(event, &attributes, &file_actions);
+ if (!errorCode) {
+ const char *path = NULL;
+ const char **argvp = NULL;
+ const char **envp = NULL;
+ errorType = 2;
+ errorCode = get_args(event, &path, &argvp, &envp);
+ if (!errorCode) {
+ errorType = 3;
+ errorCode = posix_spawn(&childPID, path, &file_actions, &attributes, (char * const *)argvp, (char * const *)envp);
+
+ if (argvp) free(argvp);
+ if (envp) free(envp);
+ }
+ }
+
+ xpc_object_t reply = xpc_dictionary_create_reply(event);
+
+ xpc_dictionary_set_int64(reply, LauncherXPCServiceChildPIDKey, childPID);
+ if (!childPID) {
+ xpc_dictionary_set_int64(reply, LauncherXPCServiceErrorTypeKey, errorType);
+ xpc_dictionary_set_int64(reply, LauncherXPCServiceCodeTypeKey, errorCode);
+ }
+
+ xpc_connection_send_message(peer, reply);
+ xpc_release(reply);
+
+ }
+}
+
+static void launcherXPC_event_handler(xpc_connection_t peer)
+{
+ // By defaults, new connections will target the default dispatch
+ // concurrent queue.
+ xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
+ launcherXPC_peer_event_handler(peer, event);
+ });
+
+ // This will tell the connection to begin listening for events. If you
+ // have some other initialization that must be done asynchronously, then
+ // you can defer this call until after that initialization is done.
+ xpc_connection_resume(peer);
+}
+
+int main(int argc, const char *argv[])
+{
+ xpc_main(launcherXPC_event_handler);
+ return 0;
+}
+#endif
diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
index 3b45691..1db2d84 100644
--- a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
+++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
@@ -258,7 +258,15 @@
}
Error
-ProcessKDP::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch)
+ProcessKDP::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info)
+{
+ Error error;
+ error.SetErrorString ("attach to process by ID is not suppported in kdp remote debugging");
+ return error;
+}
+
+Error
+ProcessKDP::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info)
{
Error error;
error.SetErrorString ("attach to process by name is not suppported in kdp remote debugging");
diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
index 5b44637..bfdea7b 100644
--- a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
+++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
@@ -96,7 +96,10 @@
DoAttachToProcessWithID (lldb::pid_t pid);
virtual lldb_private::Error
- DoAttachToProcessWithName (const char *process_name, bool wait_for_launch);
+ DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info);
+
+ virtual lldb_private::Error
+ DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const lldb_private::ProcessAttachInfo &attach_info);
virtual void
DidAttach ();
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 53b7a24..aa934e1 100644
--- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -496,7 +496,7 @@
// Make sure we aren't already connected?
if (!m_gdb_comm.IsConnected())
{
- error = StartDebugserverProcess (host_port);
+ error = StartDebugserverProcess (host_port, launch_info);
if (error.Fail())
{
if (log)
@@ -762,6 +762,13 @@
Error
ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid)
{
+ ProcessAttachInfo attach_info;
+ return DoAttachToProcessWithID(attach_pid, attach_info);
+}
+
+Error
+ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info)
+{
Error error;
// Clear out and clean up from any current state
Clear();
@@ -775,7 +782,7 @@
char connect_url[128];
snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port);
- error = StartDebugserverProcess (host_port);
+ error = StartDebugserverProcess (host_port, attach_info);
if (error.Fail())
{
@@ -824,7 +831,7 @@
}
Error
-ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch)
+ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info)
{
Error error;
// Clear out and clean up from any current state
@@ -840,7 +847,7 @@
char connect_url[128];
snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port);
- error = StartDebugserverProcess (host_port);
+ error = StartDebugserverProcess (host_port, attach_info);
if (error.Fail())
{
const char *error_string = error.AsCString();
@@ -2019,7 +2026,14 @@
}
Error
-ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url) // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...")
+ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url)
+{
+ ProcessLaunchInfo launch_info;
+ return StartDebugserverProcess(debugserver_url, launch_info);
+}
+
+Error
+ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url, const ProcessInfo &process_info) // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...")
{
Error error;
if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID)
@@ -2027,9 +2041,9 @@
// If we locate debugserver, keep that located version around
static FileSpec g_debugserver_file_spec;
- ProcessLaunchInfo launch_info;
+ ProcessLaunchInfo debugserver_launch_info;
char debugserver_path[PATH_MAX];
- FileSpec &debugserver_file_spec = launch_info.GetExecutableFile();
+ FileSpec &debugserver_file_spec = debugserver_launch_info.GetExecutableFile();
// Always check to see if we have an environment override for the path
// to the debugserver to use and use it if we do.
@@ -2067,7 +2081,7 @@
LogSP log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
- Args &debugserver_args = launch_info.GetArguments();
+ Args &debugserver_args = debugserver_launch_info.GetArguments();
char arg_cstr[PATH_MAX];
// Start args with "debugserver /file/path -r --"
@@ -2130,11 +2144,11 @@
// Close STDIN, STDOUT and STDERR. We might need to redirect them
// to "/dev/null" if we run into any problems.
file_action.Close (STDIN_FILENO);
- launch_info.AppendFileAction (file_action);
+ debugserver_launch_info.AppendFileAction (file_action);
file_action.Close (STDOUT_FILENO);
- launch_info.AppendFileAction (file_action);
+ debugserver_launch_info.AppendFileAction (file_action);
file_action.Close (STDERR_FILENO);
- launch_info.AppendFileAction (file_action);
+ debugserver_launch_info.AppendFileAction (file_action);
if (log)
{
@@ -2143,12 +2157,13 @@
log->Printf("%s arguments:\n%s", debugserver_args.GetArgumentAtIndex(0), strm.GetData());
}
- launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false);
+ debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false);
+ debugserver_launch_info.SetUserID(process_info.GetUserID());
- error = Host::LaunchProcess(launch_info);
+ error = Host::LaunchProcess(debugserver_launch_info);
if (error.Success ())
- m_debugserver_pid = launch_info.GetProcessID();
+ m_debugserver_pid = debugserver_launch_info.GetProcessID();
else
m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 894aae2..4ecd3a0 100644
--- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -103,7 +103,12 @@
DoAttachToProcessWithID (lldb::pid_t pid);
virtual lldb_private::Error
- DoAttachToProcessWithName (const char *process_name, bool wait_for_launch);
+ DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info);
+
+ virtual lldb_private::Error
+ DoAttachToProcessWithName (const char *process_name,
+ bool wait_for_launch,
+ const lldb_private::ProcessAttachInfo &attach_info);
virtual void
DidAttach ();
@@ -260,6 +265,9 @@
lldb_private::Error
StartDebugserverProcess (const char *debugserver_url);
+
+ lldb_private::Error
+ StartDebugserverProcess (const char *debugserver_url, const lldb_private::ProcessInfo &process_info);
void
KillDebugserverProcess ();
diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp
index fc498c6..b26c78e 100644
--- a/source/Target/Process.cpp
+++ b/source/Target/Process.cpp
@@ -2490,7 +2490,7 @@
m_should_detach = true;
SetPublicState (eStateAttaching);
- error = DoAttachToProcessWithName (process_name, wait_for_launch);
+ error = DoAttachToProcessWithName (process_name, wait_for_launch, attach_info);
if (error.Fail())
{
if (GetID() != LLDB_INVALID_PROCESS_ID)
@@ -2557,7 +2557,7 @@
m_should_detach = true;
SetPublicState (eStateAttaching);
- error = DoAttachToProcessWithID (attach_pid);
+ error = DoAttachToProcessWithID (attach_pid, attach_info);
if (error.Success())
{