Upstreaming the apple internal changes that accumulated during the
previous release. Most of the diffs are duplication in the xcode
project file caused by adding a "debugserver-mini" target. Jim
Ingham added support for a new SPI needed to request app launches
on iOS. Greg Clayton added code to indicate the platform of the
binary (macosx, ios, watchos, tvos) based on Mach-O load commands.
Jason Molenda added code so debugserver will identify when it is
running on a tvos/watchos device to lldb.
llvm-svn: 251091
diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp
index 53b42cd..03c85df 100644
--- a/lldb/tools/debugserver/source/DNB.cpp
+++ b/lldb/tools/debugserver/source/DNB.cpp
@@ -747,7 +747,7 @@
if (attach_token != NULL)
{
nub_process_t pid;
- pid = MachProcess::CheckForProcess(attach_token);
+ pid = MachProcess::CheckForProcess(attach_token, launch_flavor);
if (pid != INVALID_NUB_PROCESS)
{
waitfor_pid = pid;
@@ -825,7 +825,7 @@
}
bool success = waitfor_pid != INVALID_NUB_PROCESS;
- MachProcess::CleanupAfterAttach (attach_token, success, prepare_error);
+ MachProcess::CleanupAfterAttach (attach_token, launch_flavor, success, prepare_error);
return waitfor_pid;
}
diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h
index 1abcb61..e3757e9 100644
--- a/lldb/tools/debugserver/source/DNBDefs.h
+++ b/lldb/tools/debugserver/source/DNBDefs.h
@@ -96,9 +96,11 @@
eLaunchFlavorSpringBoard = 3,
#endif
#ifdef WITH_BKS
- eLaunchFlavorBKS = 4
+ eLaunchFlavorBKS = 4,
#endif
-
+#ifdef WITH_FBS
+ eLaunchFlavorFBS = 5
+#endif
} nub_launch_flavor_t;
#define NUB_STATE_IS_RUNNING(s) ((s) == eStateAttaching ||\
diff --git a/lldb/tools/debugserver/source/DNBError.cpp b/lldb/tools/debugserver/source/DNBError.cpp
index b4f3990..c9d8ebd 100644
--- a/lldb/tools/debugserver/source/DNBError.cpp
+++ b/lldb/tools/debugserver/source/DNBError.cpp
@@ -54,7 +54,17 @@
// You have to call ObjC routines to get the error string from BackBoardServices.
// Not sure I want to make DNBError.cpp an .mm file. For now just make sure you
// pre-populate the error string when you make the DNBError of type BackBoard.
- m_str.assign("Should have set Backboard error when making the error string.");
+ m_str.assign("Should have set BackBoard error when making the error string.");
+ }
+ break;
+#endif
+#ifdef WITH_FBS
+ case FrontBoard:
+ {
+ // You have to call ObjC routines to get the error string from FrontBoardServices.
+ // Not sure I want to make DNBError.cpp an .mm file. For now just make sure you
+ // pre-populate the error string when you make the DNBError of type FrontBoard.
+ m_str.assign("Should have set FrontBoard error when making the error string.");
}
break;
#endif
diff --git a/lldb/tools/debugserver/source/DNBError.h b/lldb/tools/debugserver/source/DNBError.h
index 3f8fa2f..274ae0d 100644
--- a/lldb/tools/debugserver/source/DNBError.h
+++ b/lldb/tools/debugserver/source/DNBError.h
@@ -34,6 +34,9 @@
#ifdef WITH_BKS
, BackBoard = 4
#endif
+#ifdef WITH_FBS
+ , FrontBoard = 5
+#endif
} FlavorType;
explicit DNBError( ValueType err = 0,
diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h
index 0795a78..3be0b2d 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h
@@ -14,6 +14,12 @@
#ifndef __MachProcess_h__
#define __MachProcess_h__
+#include <mach/mach.h>
+#include <sys/signal.h>
+#include <pthread.h>
+#include <vector>
+#include <CoreFoundation/CoreFoundation.h>
+
#include "DNBDefs.h"
#include "DNBBreakpoint.h"
#include "DNBError.h"
@@ -29,11 +35,6 @@
#include "ThreadInfo.h"
#include "JSONGenerator.h"
-#include <mach/mach.h>
-#include <sys/signal.h>
-#include <pthread.h>
-#include <vector>
-
class DNBThreadResumeActions;
class MachProcess
@@ -78,19 +79,24 @@
DNBError& err);
nub_addr_t GetDYLDAllImageInfosAddress ();
static const void * PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str);
- static void CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str);
- static nub_process_t CheckForProcess (const void *attach_token);
+ static void CleanupAfterAttach (const void *attach_token, nub_launch_flavor_t launch_flavor, bool success, DNBError &err_str);
+ static nub_process_t CheckForProcess (const void *attach_token, nub_launch_flavor_t launch_flavor);
+#if defined(WITH_BKS) || defined(WITH_FBS)
+ pid_t BoardServiceLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err);
+ pid_t BoardServiceForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err);
+ bool BoardServiceSendEvent (const char *event, DNBError &error);
+#endif
static bool GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *patch);
#ifdef WITH_BKS
- pid_t BKSLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err);
- pid_t BKSForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err);
- bool BKSSendEvent (const char *event, DNBError &error);
static void BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str);
-#endif
+#endif // WITH_BKS
+#ifdef WITH_FBS
+ static void FBSCleanupAfterAttach (const void *attach_token, DNBError &err_str);
+#endif // WITH_FBS
#ifdef WITH_SPRINGBOARD
pid_t SBLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, DNBError &launch_err);
static pid_t SBForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err);
-#endif
+#endif // WITH_SPRINGBOARD
nub_addr_t LookupSymbol (const char *name, const char *shlib);
void SetNameToAddressCallback (DNBCallbackNameToAddress callback, void *baton)
{
@@ -287,7 +293,8 @@
eMachProcessFlagsNone = 0,
eMachProcessFlagsAttached = (1 << 0),
eMachProcessFlagsUsingSBS = (1 << 1),
- eMachProcessFlagsUsingBKS = (1 << 2)
+ eMachProcessFlagsUsingBKS = (1 << 2),
+ eMachProcessFlagsUsingFBS = (1 << 3)
};
void Clear (bool detaching = false);
void ReplyToAllExceptions ();
diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
index c4ba9e2..b9e0630 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
+++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
@@ -44,44 +44,6 @@
#include "CFData.h"
#include "CFString.h"
-#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
-// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
-// or NULL if there was some problem getting the bundle id.
-static CFStringRef
-CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str)
-{
- CFBundle bundle(app_bundle_path);
- CFStringRef bundleIDCFStr = bundle.GetIdentifier();
- std::string bundleID;
- if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
- {
- struct stat app_bundle_stat;
- char err_msg[PATH_MAX];
-
- if (::stat (app_bundle_path, &app_bundle_stat) < 0)
- {
- err_str.SetError(errno, DNBError::POSIX);
- snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path);
- err_str.SetErrorString(err_msg);
- DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
- }
- else
- {
- err_str.SetError(-1, DNBError::Generic);
- snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path);
- err_str.SetErrorString(err_msg);
- DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
- }
- return NULL;
- }
-
- DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
- CFRetain (bundleIDCFStr);
-
- return bundleIDCFStr;
-}
-#endif // #if defined 9WITH_SPRINGBOARD) || defined (WITH_BKS)
-
#ifdef WITH_SPRINGBOARD
#include <CoreFoundation/CoreFoundation.h>
@@ -97,6 +59,123 @@
#endif // WITH_SPRINGBOARD
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
+// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
+// or NULL if there was some problem getting the bundle id.
+static CFStringRef CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str);
+#endif
+
+#if defined(WITH_BKS) || defined(WITH_FBS)
+#import <Foundation/Foundation.h>
+static const int OPEN_APPLICATION_TIMEOUT_ERROR = 111;
+typedef void (*SetErrorFunction) (NSInteger, DNBError &);
+typedef bool (*CallOpenApplicationFunction) (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid);
+// This function runs the BKSSystemService (or FBSSystemService) method openApplication:options:clientPort:withResult,
+// messaging the app passed in bundleIDNSStr.
+// The function should be run inside of an NSAutoReleasePool.
+//
+// It will use the "options" dictionary passed in, and fill the error passed in if there is an error.
+// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID.
+// If bundleIDNSStr is NULL, then the system application will be messaged.
+
+template <typename OpenFlavor, typename ErrorFlavor, ErrorFlavor no_error_enum_value, SetErrorFunction error_function>
+static bool
+CallBoardSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid)
+{
+ // Now make our systemService:
+ OpenFlavor *system_service = [[OpenFlavor alloc] init];
+
+ if (bundleIDNSStr == nil)
+ {
+ bundleIDNSStr = [system_service systemApplicationBundleIdentifier];
+ if (bundleIDNSStr == nil)
+ {
+ // Okay, no system app...
+ error.SetErrorString("No system application to message.");
+ return false;
+ }
+ }
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block ErrorFlavor open_app_error = no_error_enum_value;
+ bool wants_pid = (return_pid != NULL);
+ __block pid_t pid_in_block;
+
+ const char *cstr = [bundleIDNSStr UTF8String];
+ if (!cstr)
+ cstr = "<Unknown Bundle ID>";
+
+ DNBLog ("About to launch process for bundle ID: %s", cstr);
+ [system_service openApplication: bundleIDNSStr
+ options: options
+ clientPort: client_port
+ withResult: ^(NSError *bks_error)
+ {
+ // The system service will cleanup the client port we created for us.
+ if (bks_error)
+ open_app_error = (ErrorFlavor)[bks_error code];
+
+ if (open_app_error == no_error_enum_value)
+ {
+ if (wants_pid)
+ {
+ pid_in_block = [system_service pidForApplication: bundleIDNSStr];
+ DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
+ DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
+ }
+ else
+ DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success.");
+ }
+ else
+ {
+ const char *error_str = [(NSString *)[bks_error localizedDescription] UTF8String];
+ DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%ld).",
+ error_str ? error_str : "<unknown error>",
+ open_app_error);
+ // REMOVE ME
+ DNBLogError ("In completion handler for send event, got error \"%s\"(%ld).",
+ error_str ? error_str : "<unknown error>",
+ open_app_error);
+ }
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }
+
+ ];
+
+ const uint32_t timeout_secs = 9;
+
+ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ dispatch_release(semaphore);
+
+ if (!success)
+{
+ DNBLogError("timed out trying to send openApplication to %s.", cstr);
+ error.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ error.SetErrorString ("timed out trying to launch app");
+ }
+ else if (open_app_error != no_error_enum_value)
+ {
+ error_function (open_app_error, error);
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error);
+ success = false;
+ }
+ else if (wants_pid)
+ {
+ *return_pid = pid_in_block;
+ DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid);
+}
+
+
+ return success;
+}
+#endif
+
#ifdef WITH_BKS
#import <Foundation/Foundation.h>
extern "C"
@@ -115,10 +194,10 @@
}
static void
-SetBKSError (BKSOpenApplicationErrorCode error_code, DNBError &error)
+SetBKSError (NSInteger error_code, DNBError &error)
{
error.SetError (error_code, DNBError::BackBoard);
- NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString(error_code);
+ NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString((BKSOpenApplicationErrorCode) error_code);
const char *err_str = NULL;
if (err_nsstr == NULL)
err_str = "unknown BKS error";
@@ -131,8 +210,164 @@
error.SetErrorString(err_str);
}
-static const int BKS_OPEN_APPLICATION_TIMEOUT_ERROR = 111;
+static bool
+BKSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error)
+{
+ if (strcmp (event_data, "BackgroundContentFetching") == 0)
+ {
+ DNBLog("Setting ActivateForEvent key in options dictionary.");
+ NSDictionary *event_details = [NSDictionary dictionary];
+ NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching];
+ [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent];
+ return true;
+ }
+ else
+ {
+ DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data);
+ option_error.SetErrorString("Unrecognized event data.");
+ return false;
+ }
+
+}
+
+static NSMutableDictionary *
+BKSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSMutableDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data)
+{
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ if (launch_argv != nil)
+ [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments];
+ if (launch_envp != nil)
+ [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment];
+
+ [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath];
+ [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger];
+ if (disable_aslr)
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
+ // And there are some other options at the top level in this dictionary:
+ [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice];
+
+ DNBError error;
+ BKSAddEventDataToOptions (options, event_data, error);
+
+ return options;
+}
+
+static CallOpenApplicationFunction BKSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication<BKSSystemService, BKSOpenApplicationErrorCode, BKSOpenApplicationErrorCodeNone, SetBKSError>;
#endif // WITH_BKS
+
+#ifdef WITH_FBS
+#import <Foundation/Foundation.h>
+extern "C"
+{
+#import <FrontBoardServices/FrontBoardServices.h>
+#import <FrontBoardServices/FBSSystemService_LaunchServices.h>
+#import <FrontBoardServices/FBSOpenApplicationConstants_Private.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+#import <MobileCoreServices/LSResourceProxy.h>
+}
+
+#ifdef WITH_BKS
+static bool
+IsFBSProcess (nub_process_t pid)
+{
+ BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init];
+ BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid];
+ return app_state != BKSApplicationStateUnknown;
+}
+#else
+static bool
+IsFBSProcess (nub_process_t pid)
+{
+ // FIXME: What is the FBS equivalent of BKSApplicationStateMonitor
+ return true;
+}
+#endif
+
+static void
+SetFBSError (NSInteger error_code, DNBError &error)
+{
+ error.SetError ((DNBError::ValueType) error_code, DNBError::FrontBoard);
+ NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString((FBSOpenApplicationErrorCode) error_code);
+ const char *err_str = NULL;
+ if (err_nsstr == NULL)
+ err_str = "unknown FBS error";
+ else
+ {
+ err_str = [err_nsstr UTF8String];
+ if (err_str == NULL)
+ err_str = "unknown FBS error";
+ }
+ error.SetErrorString(err_str);
+}
+
+static bool
+FBSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error)
+{
+ if (strcmp (event_data, "BackgroundContentFetching") == 0)
+ {
+ DNBLog("Setting ActivateForEvent key in options dictionary.");
+ NSDictionary *event_details = [NSDictionary dictionary];
+ NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:FBSActivateForEventOptionTypeBackgroundContentFetching];
+ [options setObject: event_dictionary forKey: FBSOpenApplicationOptionKeyActivateForEvent];
+ return true;
+ }
+ else
+ {
+ DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data);
+ option_error.SetErrorString("Unrecognized event data.");
+ return false;
+ }
+
+}
+
+static NSMutableDictionary *
+FBSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data)
+{
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+
+ if (launch_argv != nil)
+ [debug_options setObject: launch_argv forKey: FBSDebugOptionKeyArguments];
+ if (launch_envp != nil)
+ [debug_options setObject: launch_envp forKey: FBSDebugOptionKeyEnvironment];
+
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath];
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger];
+ if (disable_aslr)
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDisableASLR];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions];
+ // And there are some other options at the top level in this dictionary:
+ [options setObject: [NSNumber numberWithBool: YES] forKey: FBSOpenApplicationOptionKeyUnlockDevice];
+
+ // We have to get the "sequence ID & UUID" for this app bundle path and send them to FBS:
+
+ NSURL *app_bundle_url = [NSURL fileURLWithPath: [NSString stringWithUTF8String: app_bundle_path] isDirectory: YES];
+ LSApplicationProxy *app_proxy = [LSApplicationProxy applicationProxyForBundleURL: app_bundle_url];
+ if (app_proxy)
+ {
+ DNBLog("Sending AppProxy info: sequence no: %lu, GUID: %s.", app_proxy.sequenceNumber, [app_proxy.cacheGUID.UUIDString UTF8String]);
+ [options setObject: [NSNumber numberWithUnsignedInteger: app_proxy.sequenceNumber] forKey: FBSOpenApplicationOptionKeyLSSequenceNumber];
+ [options setObject: app_proxy.cacheGUID.UUIDString forKey: FBSOpenApplicationOptionKeyLSCacheGUID];
+ }
+
+ DNBError error;
+ FBSAddEventDataToOptions (options, event_data, error);
+
+ return options;
+}
+static CallOpenApplicationFunction FBSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication<FBSSystemService, FBSOpenApplicationErrorCode, FBSOpenApplicationErrorCodeNone, SetFBSError>;
+#endif // WITH_FBS
+
#if 0
#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
#else
@@ -863,8 +1098,9 @@
DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SendEvent (event = %s) to pid: %d", event, m_pid);
if (m_pid == INVALID_NUB_PROCESS)
return false;
-#if WITH_BKS
- return BKSSendEvent (event, send_err);
+ // FIXME: Shouldn't we use the launch flavor we were started with?
+#if defined(WITH_FBS) || defined(WITH_BKS)
+ return BoardServiceSendEvent (event, send_err);
#endif
return true;
}
@@ -1970,9 +2206,22 @@
SetState(eStateAttaching);
m_pid = pid;
// Let ourselves know we are going to be using SBS or BKS if the correct flag bit is set...
-#if defined (WITH_BKS)
- if (IsBKSProcess (pid))
+#if defined (WITH_FBS) || defined (WITH_BKS)
+ bool found_app_flavor = false;
+#endif
+
+#if defined (WITH_FBS)
+ if (!found_app_flavor && IsFBSProcess (pid))
+ {
+ found_app_flavor = true;
+ m_flags |= eMachProcessFlagsUsingFBS;
+ }
+#elif defined (WITH_BKS)
+ if (!found_app_flavor && IsBKSProcess (pid))
+ {
+ found_app_flavor = true;
m_flags |= eMachProcessFlagsUsingBKS;
+ }
#elif defined (WITH_SPRINGBOARD)
if (IsSBProcess(pid))
m_flags |= eMachProcessFlagsUsingSBS;
@@ -2058,7 +2307,7 @@
const void *
MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &attach_err)
{
-#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
// Tell SpringBoard to halt the next launch of this application on startup.
if (!waitfor)
@@ -2074,7 +2323,12 @@
return NULL;
}
-#if defined (WITH_BKS)
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorDefault)
+ launch_flavor = eLaunchFlavorFBS;
+ if (launch_flavor != eLaunchFlavorFBS)
+ return NULL;
+#elif defined (WITH_BKS)
if (launch_flavor == eLaunchFlavorDefault)
launch_flavor = eLaunchFlavorBKS;
if (launch_flavor != eLaunchFlavorBKS)
@@ -2101,6 +2355,76 @@
return NULL;
}
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS)
+ {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *stdio_path = nil;
+ NSFileManager *file_manager = [NSFileManager defaultManager];
+ const char *null_path = "/dev/null";
+ stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)];
+
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+
+ DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", "
+ "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )",
+ bundleIDStr.c_str(),
+ null_path);
+
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath];
+ [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDebugOnNextLaunch];
+
+ [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions];
+
+ FBSSystemService *system_service = [[FBSSystemService alloc] init];
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block FBSOpenApplicationErrorCode attach_error_code = FBSOpenApplicationErrorCodeNone;
+
+ NSString *bundleIDNSStr = (NSString *) bundleIDCFStr;
+
+ [system_service openApplication: bundleIDNSStr
+ options: options
+ clientPort: client_port
+ withResult: ^(NSError *error)
+ {
+ // The system service will cleanup the client port we created for us.
+ if (error)
+ attach_error_code = (FBSOpenApplicationErrorCode)[error code];
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }
+ ];
+
+ const uint32_t timeout_secs = 9;
+
+ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ if (!success)
+ {
+ DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
+ attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete.");
+ attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ }
+ else if (attach_error_code != FBSOpenApplicationErrorCodeNone)
+ {
+ SetFBSError (attach_error_code, attach_err);
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld",
+ bundleIDStr.c_str(),
+ (NSInteger) attach_error_code);
+ }
+ dispatch_release(semaphore);
+ [pool drain];
+ }
+#endif
#if defined (WITH_BKS)
if (launch_flavor == eLaunchFlavorBKS)
{
@@ -2158,19 +2482,21 @@
{
DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete.");
- attach_err.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
}
else if (attach_error_code != BKSOpenApplicationErrorCodeNone)
{
SetBKSError (attach_error_code, attach_err);
- DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u",
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld",
bundleIDStr.c_str(),
attach_error_code);
}
dispatch_release(semaphore);
[pool drain];
}
-#elif defined (WITH_SPRINGBOARD)
+#endif
+
+#if defined (WITH_SPRINGBOARD)
if (launch_flavor == eLaunchFlavorSpringBoard)
{
SBSApplicationLaunchError sbs_error = 0;
@@ -2203,7 +2529,7 @@
DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
return bundleIDCFStr;
-# else // defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
+# else // !(defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS))
return NULL;
#endif
}
@@ -2213,12 +2539,28 @@
// will be returned.
nub_process_t
-MachProcess::CheckForProcess (const void *attach_token)
+MachProcess::CheckForProcess (const void *attach_token, nub_launch_flavor_t launch_flavor)
{
if (attach_token == NULL)
return INVALID_NUB_PROCESS;
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS)
+ {
+ NSString *bundleIDNSStr = (NSString *) attach_token;
+ FBSSystemService *systemService = [[FBSSystemService alloc] init];
+ pid_t pid = [systemService pidForApplication: bundleIDNSStr];
+ [systemService release];
+ if (pid == 0)
+ return INVALID_NUB_PROCESS;
+ else
+ return pid;
+ }
+#endif
+
#if defined (WITH_BKS)
+ if (launch_flavor == eLaunchFlavorBKS)
+ {
NSString *bundleIDNSStr = (NSString *) attach_token;
BKSSystemService *systemService = [[BKSSystemService alloc] init];
pid_t pid = [systemService pidForApplication: bundleIDNSStr];
@@ -2227,7 +2569,12 @@
return INVALID_NUB_PROCESS;
else
return pid;
-#elif defined (WITH_SPRINGBOARD)
+ }
+#endif
+
+#if defined (WITH_SPRINGBOARD)
+ if (launch_flavor == eLaunchFlavorSpringBoard)
+ {
CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
Boolean got_it;
nub_process_t attach_pid;
@@ -2236,9 +2583,9 @@
return attach_pid;
else
return INVALID_NUB_PROCESS;
-#else
- return INVALID_NUB_PROCESS;
+ }
#endif
+ return INVALID_NUB_PROCESS;
}
// Call this to clean up after you have either attached or given up on the attach.
@@ -2247,22 +2594,39 @@
// this method.
void
-MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str)
+MachProcess::CleanupAfterAttach (const void *attach_token, nub_launch_flavor_t launch_flavor, bool success, DNBError &err_str)
{
if (attach_token == NULL)
return;
+#if defined (WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS)
+ {
+ if (!success)
+ {
+ FBSCleanupAfterAttach (attach_token, err_str);
+ }
+ CFRelease((CFStringRef) attach_token);
+ }
+#endif
+
#if defined (WITH_BKS)
+ if (launch_flavor == eLaunchFlavorBKS)
+ {
if (!success)
{
BKSCleanupAfterAttach (attach_token, err_str);
}
CFRelease((CFStringRef) attach_token);
+ }
+#endif
-#elif defined (WITH_SPRINGBOARD)
+#if defined (WITH_SPRINGBOARD)
// Tell SpringBoard to cancel the debug on next launch of this application
// if we failed to attach
+ if (launch_flavor == eMachProcessFlagsUsingSpringBoard)
+ {
if (!success)
{
SBSApplicationLaunchError sbs_error = 0;
@@ -2284,6 +2648,7 @@
}
CFRelease((CFStringRef) attach_token);
+ }
#endif
}
@@ -2317,6 +2682,22 @@
case eLaunchFlavorForkExec:
m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err);
break;
+#ifdef WITH_FBS
+ case eLaunchFlavorFBS:
+ {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/'))
+ {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ m_flags |= eMachProcessFlagsUsingFBS;
+ if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
+ else
+ break; // We tried a FBS launch, but didn't succeed lets get out
+ }
+ }
+ break;
+#endif
#ifdef WITH_BKS
case eLaunchFlavorBKS:
{
@@ -2324,7 +2705,8 @@
if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/'))
{
std::string app_bundle_path(path, app_ext + strlen(".app"));
- if (BKSLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
+ m_flags |= eMachProcessFlagsUsingBKS;
+ if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0)
return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid.
else
break; // We tried a BKS launch, but didn't succeed lets get out
@@ -2719,6 +3101,43 @@
return pid;
}
+#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
+// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
+// or NULL if there was some problem getting the bundle id.
+static CFStringRef
+CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str)
+{
+ CFBundle bundle(app_bundle_path);
+ CFStringRef bundleIDCFStr = bundle.GetIdentifier();
+ std::string bundleID;
+ if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
+ {
+ struct stat app_bundle_stat;
+ char err_msg[PATH_MAX];
+
+ if (::stat (app_bundle_path, &app_bundle_stat) < 0)
+ {
+ err_str.SetError(errno, DNBError::POSIX);
+ snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
+ }
+ else
+ {
+ err_str.SetError(-1, DNBError::Generic);
+ snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
+ }
+ return NULL;
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
+ CFRetain (bundleIDCFStr);
+
+ return bundleIDCFStr;
+}
+#endif // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)
#ifdef WITH_SPRINGBOARD
pid_t
@@ -2929,178 +3348,19 @@
#endif // #ifdef WITH_SPRINGBOARD
-#ifdef WITH_BKS
-
-
-// This function runs the BKSSystemService method openApplication:options:clientPort:withResult,
-// messaging the app passed in bundleIDNSStr.
-// The function should be run inside of an NSAutoReleasePool.
-//
-// It will use the "options" dictionary passed in, and fill the error passed in if there is an error.
-// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID.
-// If bundleIDNSStr is NULL, then the system application will be messaged.
-
-static bool
-CallBKSSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid)
-{
- // Now make our systemService:
- BKSSystemService *system_service = [[BKSSystemService alloc] init];
-
- if (bundleIDNSStr == nil)
- {
- bundleIDNSStr = [system_service systemApplicationBundleIdentifier];
- if (bundleIDNSStr == nil)
- {
- // Okay, no system app...
- error.SetErrorString("No system application to message.");
- return false;
- }
- }
-
- mach_port_t client_port = [system_service createClientPort];
- __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
- __block BKSOpenApplicationErrorCode open_app_error = BKSOpenApplicationErrorCodeNone;
- bool wants_pid = (return_pid != NULL);
- __block pid_t pid_in_block;
-
- const char *cstr = [bundleIDNSStr UTF8String];
- if (!cstr)
- cstr = "<Unknown Bundle ID>";
-
- DNBLog ("About to launch process for bundle ID: %s", cstr);
- [system_service openApplication: bundleIDNSStr
- options: options
- clientPort: client_port
- withResult: ^(NSError *bks_error)
- {
- // The system service will cleanup the client port we created for us.
- if (bks_error)
- open_app_error = (BKSOpenApplicationErrorCode)[bks_error code];
-
- if (open_app_error == BKSOpenApplicationErrorCodeNone)
- {
- if (wants_pid)
- {
- pid_in_block = [system_service pidForApplication: bundleIDNSStr];
- DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
- DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block);
- }
- else
- DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success.");
- }
- else
- {
- const char *error_str = [[bks_error localizedDescription] UTF8String];
- DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%d).",
- error_str ? error_str : "<unknown error>",
- open_app_error);
- // REMOVE ME
- DNBLogError ("In completion handler for send event, got error \"%s\"(%d).",
- error_str ? error_str : "<unknown error>",
- open_app_error);
- }
-
- [system_service release];
- dispatch_semaphore_signal(semaphore);
- }
-
- ];
-
- const uint32_t timeout_secs = 9;
-
- dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
-
- long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
-
- dispatch_release(semaphore);
-
- if (!success)
- {
- DNBLogError("timed out trying to send openApplication to %s.", cstr);
- error.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
- error.SetErrorString ("timed out trying to launch app");
- }
- else if (open_app_error != BKSOpenApplicationErrorCodeNone)
- {
- SetBKSError (open_app_error, error);
- DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error);
- success = false;
- }
- else if (wants_pid)
- {
- *return_pid = pid_in_block;
- DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid);
- }
- return success;
-}
-
-void
-MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str)
-{
- bool success;
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
- NSString *bundleIDNSStr = (NSString *) attach_token;
-
- // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
-
- // First we have the debug sub-dictionary:
- NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
- [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch];
-
- // That will go in the overall dictionary:
-
- NSMutableDictionary *options = [NSMutableDictionary dictionary];
- [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
-
- success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, err_str, NULL);
-
- if (!success)
- {
- DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString());
- }
-
- [pool drain];
-}
-
-bool
-AddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error)
-{
- if (strcmp (event_data, "BackgroundContentFetching") == 0)
- {
- DNBLog("Setting ActivateForEvent key in options dictionary.");
- NSDictionary *event_details = [NSDictionary dictionary];
- NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching];
- [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent];
- return true;
- }
- else
- {
- DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data);
- option_error.SetErrorString("Unrecognized event data.");
- return false;
- }
-
-}
-
+#if defined (WITH_BKS) || defined (WITH_FBS)
pid_t
-MachProcess::BKSLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err)
+MachProcess::BoardServiceLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err)
{
- // Clear out and clean up from any current state
- Clear();
-
DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
// Fork a child process for debugging
SetState(eStateLaunching);
- m_pid = BKSForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err);
+ m_pid = BoardServiceForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err);
if (m_pid != 0)
{
- m_flags |= eMachProcessFlagsUsingBKS;
m_path = path;
size_t i;
char const *arg;
@@ -3136,7 +3396,7 @@
}
pid_t
-MachProcess::BKSForkChildForPTraceDebugging (const char *app_bundle_path,
+MachProcess::BoardServiceForkChildForPTraceDebugging (const char *app_bundle_path,
char const *argv[],
char const *envp[],
bool no_stdio,
@@ -3242,40 +3502,24 @@
// Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
- // First we have the debug sub-dictionary:
- NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
- if (launch_argv != nil)
- [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments];
- if (launch_envp != nil)
- [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment];
-
- [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath];
- [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath];
- [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger];
- if (disable_aslr)
- [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR];
-
- // That will go in the overall dictionary:
-
- NSMutableDictionary *options = [NSMutableDictionary dictionary];
- [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
-
- // For now we only support one kind of event: the "fetch" event, which is indicated by the fact that its data
- // is an empty dictionary.
- if (event_data != NULL && *event_data != '\0')
- {
- if (!AddEventDataToOptions(options, event_data, launch_err))
- {
- [pool drain];
- return INVALID_NUB_PROCESS;
- }
- }
-
- // And there are some other options at the top level in this dictionary:
- [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice];
-
+ NSMutableDictionary *options = nullptr;
pid_t return_pid = INVALID_NUB_PROCESS;
- bool success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, launch_err, &return_pid);
+ bool success = false;
+
+#ifdef WITH_BKS
+ if (m_flags & eMachProcessFlagsUsingBKS)
+ {
+ options = BKSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data);
+ success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid);
+ }
+#endif
+#ifdef WITH_FBS
+ if (m_flags & eMachProcessFlagsUsingFBS)
+ {
+ options = FBSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data);
+ success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid);
+ }
+#endif
if (success)
{
@@ -3290,7 +3534,7 @@
}
bool
-MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err)
+MachProcess::BoardServiceSendEvent (const char *event_data, DNBError &send_err)
{
bool return_value = true;
@@ -3306,7 +3550,18 @@
if (strcmp (event_data, "BackgroundApplication") == 0)
{
// This is an event I cooked up. What you actually do is foreground the system app, so:
- return_value = CallBKSSystemServiceOpenApplication(nil, nil, send_err, NULL);
+#ifdef WITH_BKS
+ if (m_flags & eMachProcessFlagsUsingBKS)
+ {
+ return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL);
+ }
+#endif
+#ifdef WITH_FBS
+ if (m_flags & eMachProcessFlagsUsingFBS)
+ {
+ return_value = FBSCallOpenApplicationFunction(nil, nil, send_err, NULL);
+ }
+#endif
if (!return_value)
{
DNBLogError ("Failed to background application, error: %s.", send_err.AsString());
@@ -3326,14 +3581,31 @@
NSMutableDictionary *options = [NSMutableDictionary dictionary];
- if (!AddEventDataToOptions(options, event_data, send_err))
+#ifdef WITH_BKS
+ if (m_flags & eMachProcessFlagsUsingBKS)
+ {
+ if (!BKSAddEventDataToOptions(options, event_data, send_err))
{
[pool drain];
return false;
}
+ return_value = BKSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL);
+ DNBLogThreadedIf (LOG_PROCESS, "Called BKSCallOpenApplicationFunction to send event.");
-
- return_value = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, send_err, NULL);
+ }
+#endif
+#ifdef WITH_FBS
+ if (m_flags & eMachProcessFlagsUsingFBS)
+ {
+ if (!FBSAddEventDataToOptions(options, event_data, send_err))
+ {
+ [pool drain];
+ return false;
+ }
+ return_value = FBSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL);
+ DNBLogThreadedIf (LOG_PROCESS, "Called FBSCallOpenApplicationFunction to send event.");
+ }
+#endif
if (!return_value)
{
@@ -3344,4 +3616,70 @@
[pool drain];
return return_value;
}
+#endif // defined(WITH_BKS) || defined (WITH_FBS)
+
+#ifdef WITH_BKS
+void
+MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str)
+{
+ bool success;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *) attach_token;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
+
+ // First we have the debug sub-dictionary:
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions];
+
+ success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL);
+
+ if (!success)
+ {
+ DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString());
+ }
+
+ [pool drain];
+}
#endif // WITH_BKS
+
+#ifdef WITH_FBS
+void
+MachProcess::FBSCleanupAfterAttach (const void *attach_token, DNBError &err_str)
+{
+ bool success;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *) attach_token;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary:
+
+ // First we have the debug sub-dictionary:
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyCancelDebugOnNextLaunch];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions];
+
+ success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL);
+
+ if (!success)
+ {
+ DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString());
+ }
+
+ [pool drain];
+}
+#endif // WITH_FBS
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index 1f751de..2241fa0 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -17,6 +17,7 @@
#include <unistd.h>
#include <signal.h>
#include <mach/exception_types.h>
+#include <mach-o/loader.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
@@ -4608,7 +4609,14 @@
// this for now.
if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64)
{
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+ strm << "ostype:tvos;";
+#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ strm << "ostype:watchos;";
+#else
strm << "ostype:ios;";
+#endif
+
// On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.
strm << "watchpoint_exceptions_received:before;";
}
@@ -4655,6 +4663,11 @@
strm << "ptrsize:8;";
else
strm << "ptrsize:" << std::dec << sizeof(void *) << ';';
+
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ strm << "default_packet_timeout:10;";
+#endif
+
return SendPacket (strm.str());
}
@@ -5514,6 +5527,138 @@
return SendPacket ("OK");
}
+static bool
+MachHeaderIsMainExecutable (nub_process_t pid, uint32_t addr_size, nub_addr_t mach_header_addr, mach_header &mh)
+{
+ DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx)", pid, addr_size, mach_header_addr);
+ const nub_size_t bytes_read = DNBProcessMemoryRead(pid, mach_header_addr, sizeof(mh), &mh);
+ if (bytes_read == sizeof(mh))
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx): mh = {\n magic = 0x%8.8x\n cpu = 0x%8.8x\n sub = 0x%8.8x\n filetype = %u\n ncmds = %u\n sizeofcmds = 0x%8.8x\n flags = 0x%8.8x }", pid, addr_size, mach_header_addr, mh.magic, mh.cputype, mh.cpusubtype, mh.filetype, mh.ncmds, mh.sizeofcmds, mh.flags);
+ if ((addr_size == 4 && mh.magic == MH_MAGIC) ||
+ (addr_size == 8 && mh.magic == MH_MAGIC_64))
+ {
+ if (mh.filetype == MH_EXECUTE)
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = %u, mach_header_addr = 0x%16.16llx) -> this is the executable!!!", pid, addr_size, mach_header_addr);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static nub_addr_t
+GetMachHeaderForMainExecutable (const nub_process_t pid, const uint32_t addr_size, mach_header &mh)
+{
+ struct AllImageInfos
+ {
+ uint32_t version;
+ uint32_t dylib_info_count;
+ uint64_t dylib_info_addr;
+ };
+
+ uint64_t mach_header_addr = 0;
+
+ const nub_addr_t shlib_addr = DNBProcessGetSharedLibraryInfoAddress (pid);
+ uint8_t bytes[256];
+ nub_size_t bytes_read = 0;
+ DNBDataRef data (bytes, sizeof(bytes), false);
+ DNBDataRef::offset_t offset = 0;
+ data.SetPointerSize(addr_size);
+
+ //----------------------------------------------------------------------
+ // When we are sitting at __dyld_start, the kernel has placed the
+ // address of the mach header of the main executable on the stack. If we
+ // read the SP and dereference a pointer, we might find the mach header
+ // for the executable. We also just make sure there is only 1 thread
+ // since if we are at __dyld_start we shouldn't have multiple threads.
+ //----------------------------------------------------------------------
+ if (DNBProcessGetNumThreads(pid) == 1)
+ {
+ nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, 0);
+ if (tid != INVALID_NUB_THREAD)
+ {
+ DNBRegisterValue sp_value;
+ if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_SP, &sp_value))
+ {
+ uint64_t sp = addr_size == 8 ? sp_value.value.uint64 : sp_value.value.uint32;
+ bytes_read = DNBProcessMemoryRead(pid, sp, addr_size, bytes);
+ if (bytes_read == addr_size)
+ {
+ offset = 0;
+ mach_header_addr = data.GetPointer(&offset);
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Check the dyld_all_image_info structure for a list of mach header
+ // since it is a very easy thing to check
+ //----------------------------------------------------------------------
+ if (shlib_addr != INVALID_NUB_ADDRESS)
+ {
+ bytes_read = DNBProcessMemoryRead(pid, shlib_addr, sizeof(AllImageInfos), bytes);
+ if (bytes_read > 0)
+ {
+ AllImageInfos aii;
+ offset = 0;
+ aii.version = data.Get32(&offset);
+ aii.dylib_info_count = data.Get32(&offset);
+ if (aii.dylib_info_count > 0)
+ {
+ aii.dylib_info_addr = data.GetPointer(&offset);
+ if (aii.dylib_info_addr != 0)
+ {
+ const size_t image_info_byte_size = 3 * addr_size;
+ for (uint32_t i=0; i<aii.dylib_info_count; ++i)
+ {
+ bytes_read = DNBProcessMemoryRead(pid, aii.dylib_info_addr + i * image_info_byte_size, image_info_byte_size, bytes);
+ if (bytes_read != image_info_byte_size)
+ break;
+ offset = 0;
+ mach_header_addr = data.GetPointer(&offset);
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ }
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // We failed to find the executable's mach header from the all image
+ // infos and by dereferencing the stack pointer. Now we fall back to
+ // enumerating the memory regions and looking for regions that are
+ // executable.
+ //----------------------------------------------------------------------
+ DNBRegionInfo region_info;
+ mach_header_addr = 0;
+ while (DNBProcessMemoryRegionInfo(pid, mach_header_addr, ®ion_info))
+ {
+ if (region_info.size == 0)
+ break;
+
+ if (region_info.permissions & eMemoryPermissionsExecutable)
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx) permissions = %c%c%c: checking region for executable mach header", region_info.addr, region_info.addr + region_info.size, (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ }
+ else
+ {
+ DNBLogThreadedIf (LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx): permissions = %c%c%c: skipping region", region_info.addr, region_info.addr + region_info.size, (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
+ }
+ // Set the address to the next mapped region
+ mach_header_addr = region_info.addr + region_info.size;
+ }
+ bzero (&mh, sizeof(mh));
+ return INVALID_NUB_ADDRESS;
+}
+
rnb_err_t
RNBRemote::HandlePacket_qSymbol (const char *command)
{
@@ -5593,7 +5738,7 @@
pid = m_ctx.ProcessID();
- rep << "pid:" << std::hex << pid << ";";
+ rep << "pid:" << std::hex << pid << ';';
int procpid_mib[4];
procpid_mib[0] = CTL_KERN;
@@ -5607,12 +5752,12 @@
{
if (proc_kinfo_size > 0)
{
- rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ";";
- rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid << ";";
- rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid << ";";
- rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid << ";";
+ rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ';';
+ rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid << ';';
+ rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid << ';';
+ rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid << ';';
if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
- rep << "effective-gid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ";";
+ rep << "effective-gid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ';';
}
}
@@ -5623,9 +5768,14 @@
cputype = best_guess_cpu_type();
}
+ uint32_t addr_size = 0;
if (cputype != 0)
{
rep << "cputype:" << std::hex << cputype << ";";
+ if (cputype & CPU_ARCH_ABI64)
+ addr_size = 8;
+ else
+ addr_size = 4;
}
bool host_cpu_is_64bit = false;
@@ -5660,11 +5810,82 @@
rep << "cpusubtype:" << std::hex << cpusubtype << ';';
}
+ bool os_handled = false;
+ if (addr_size > 0)
+ {
+ rep << "ptrsize:" << std::dec << addr_size << ';';
+
+#if (defined (__x86_64__) || defined (__i386__))
+ // Try and get the OS type by looking at the load commands in the main
+ // executable and looking for a LC_VERSION_MIN load command. This is the
+ // most reliable way to determine the "ostype" value when on desktop.
+
+ mach_header mh;
+ nub_addr_t exe_mach_header_addr = GetMachHeaderForMainExecutable (pid, addr_size, mh);
+ if (exe_mach_header_addr != INVALID_NUB_ADDRESS)
+ {
+ uint64_t load_command_addr = exe_mach_header_addr + ((addr_size == 8) ? sizeof(mach_header_64) : sizeof(mach_header));
+ load_command lc;
+ for (uint32_t i=0; i<mh.ncmds && !os_handled; ++i)
+ {
+ const nub_size_t bytes_read = DNBProcessMemoryRead (pid, load_command_addr, sizeof(lc), &lc);
+ uint32_t raw_cmd = lc.cmd & ~LC_REQ_DYLD;
+ if (bytes_read != sizeof(lc))
+ break;
+ switch (raw_cmd)
+ {
+ case LC_VERSION_MIN_IPHONEOS:
+ os_handled = true;
+ rep << "ostype:ios;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_IPHONEOS -> 'ostype:ios;'");
+ break;
+
+ case LC_VERSION_MIN_MACOSX:
+ os_handled = true;
+ rep << "ostype:macosx;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_MACOSX -> 'ostype:macosx;'");
+ break;
+
+#if defined (DT_VARIANT_PONDEROSA) || TARGET_OS_TV == 1
+ case LC_VERSION_MIN_TVOS:
+ os_handled = true;
+ rep << "ostype:tvos;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_TVOS -> 'ostype:tvos;'");
+ break;
+#endif
+
+ case LC_VERSION_MIN_WATCHOS:
+ os_handled = true;
+ rep << "ostype:watchos;";
+ DNBLogThreadedIf (LOG_RNB_PROC, "LC_VERSION_MIN_WATCHOS -> 'ostype:watchos;'");
+ break;
+
+ default:
+ break;
+ }
+ load_command_addr = load_command_addr + lc.cmdsize;
+ }
+ }
+#endif
+ }
+
+ // If we weren't able to find the OS in a LC_VERSION_MIN load command, try
+ // to set it correctly by using the cpu type and other tricks
+ if (!os_handled)
+ {
// The OS in the triple should be "ios" or "macosx" which doesn't match our
// "Darwin" which gets returned from "kern.ostype", so we need to hardcode
// this for now.
if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64)
+ {
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+ rep << "ostype:tvos;";
+#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ rep << "ostype:watchos;";
+#else
rep << "ostype:ios;";
+#endif
+ }
else
{
bool is_ios_simulator = false;
@@ -5716,10 +5937,21 @@
}
}
if (is_ios_simulator)
+ {
+#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1
+ rep << "ostype:tvos;";
+#elif defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ rep << "ostype:watchos;";
+#else
rep << "ostype:ios;";
+#endif
+ }
else
+ {
rep << "ostype:macosx;";
}
+ }
+ }
rep << "vendor:apple;";
@@ -5731,6 +5963,8 @@
rep << "endian:pdp;";
#endif
+ if (addr_size == 0)
+ {
#if (defined (__x86_64__) || defined (__i386__)) && defined (x86_THREAD_STATE)
nub_thread_t thread = DNBProcessGetCurrentThreadMachPort (pid);
kern_return_t kr;
@@ -5764,6 +5998,7 @@
rep << "ptrsize:4;";
}
#endif
+ }
return SendPacket (rep.str());
}
diff --git a/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist b/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist
index 6fb9aff..e9a74bd 100644
--- a/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist
+++ b/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist
@@ -10,5 +10,7 @@
<string>--lockdown</string>
<string>--applist</string>
</array>
+ <key>AllowByProxy</key>
+ <true/>
</dict>
</plist>
diff --git a/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist b/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist
index bd4037c..002e90d 100644
--- a/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist
+++ b/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist
@@ -11,7 +11,9 @@
<string>/Developer/usr/bin/debugserver</string>
<string>--lockdown</string>
<string>--applist</string>
- <string>--launch=backboard</string>
+ <string>--launch=frontboard</string>
</array>
+ <key>AllowByProxy</key>
+ <true/>
</dict>
</plist>
diff --git a/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist b/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist
index 923dc58..b9f57f7 100644
--- a/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist
+++ b/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist
@@ -9,5 +9,7 @@
<string>/Developer/usr/bin/debugserver</string>
<string>--lockdown</string>
</array>
+ <key>AllowByProxy</key>
+ <true/>
</dict>
</plist>
diff --git a/lldb/tools/debugserver/source/com.apple.debugserver.plist b/lldb/tools/debugserver/source/com.apple.debugserver.plist
index f61a2ac..c07466e 100644
--- a/lldb/tools/debugserver/source/com.apple.debugserver.plist
+++ b/lldb/tools/debugserver/source/com.apple.debugserver.plist
@@ -10,7 +10,9 @@
<array>
<string>/Developer/usr/bin/debugserver</string>
<string>--lockdown</string>
- <string>--launch=backboard</string>
+ <string>--launch=frontboard</string>
</array>
+ <key>AllowByProxy</key>
+ <true/>
</dict>
</plist>
diff --git a/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist b/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist
index 9e4ca77..4083f8a 100644
--- a/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist
+++ b/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist
@@ -12,5 +12,7 @@
<string>--lockdown</string>
<string>--launch=posix</string>
</array>
+ <key>AllowByProxy</key>
+ <true/>
</dict>
</plist>
diff --git a/lldb/tools/debugserver/source/debugserver-entitlements.plist b/lldb/tools/debugserver/source/debugserver-entitlements.plist
index 84e0ca4..1b1fa6c 100644
--- a/lldb/tools/debugserver/source/debugserver-entitlements.plist
+++ b/lldb/tools/debugserver/source/debugserver-entitlements.plist
@@ -8,6 +8,10 @@
<true/>
<key>com.apple.backboardd.debugapplications</key>
<true/>
+ <key>com.apple.frontboard.launchapplications</key>
+ <true/>
+ <key>com.apple.frontboard.debugapplications</key>
+ <true/>
<key>run-unsigned-code</key>
<true/>
<key>seatbelt-profiles</key>
diff --git a/lldb/tools/debugserver/source/debugserver.cpp b/lldb/tools/debugserver/source/debugserver.cpp
index afd5b00..33d39af 100644
--- a/lldb/tools/debugserver/source/debugserver.cpp
+++ b/lldb/tools/debugserver/source/debugserver.cpp
@@ -202,7 +202,13 @@
// Our default launch method is posix spawn
launch_flavor = eLaunchFlavorPosixSpawn;
-#if defined WITH_BKS
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(inferior_argv[0], ".app"))
+ {
+ launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
// Check if we have an app bundle, if so launch using BackBoard Services.
if (strstr(inferior_argv[0], ".app"))
{
@@ -1094,6 +1100,10 @@
else if (strcasestr(optarg, "backboard") == optarg)
g_launch_flavor = eLaunchFlavorBKS;
#endif
+#ifdef WITH_FBS
+ else if (strcasestr(optarg, "frontboard") == optarg)
+ g_launch_flavor = eLaunchFlavorFBS;
+#endif
else
{
@@ -1108,6 +1118,9 @@
#ifdef WITH_BKS
RNBLogSTDERR (" backboard Launch the executable through BackBoard Services.\n");
#endif
+#ifdef WITH_FBS
+ RNBLogSTDERR (" frontboard Launch the executable through FrontBoard Services.\n");
+#endif
exit (5);
}
}
@@ -1484,7 +1497,13 @@
// Our default launch method is posix spawn
launch_flavor = eLaunchFlavorPosixSpawn;
-#if defined WITH_BKS
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find (".app") != std::string::npos)
+ {
+ launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
// Check if we have an app bundle, if so launch using SpringBoard.
if (waitfor_pid_name.find (".app") != std::string::npos)
{