1//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9//  Created by Greg Clayton on 6/15/07.
10//
11//===----------------------------------------------------------------------===//
12
13#include "DNB.h"
14#include "MacOSX/CFUtils.h"
15#include "SysSignal.h"
16#include <dlfcn.h>
17#include <inttypes.h>
18#include <mach-o/loader.h>
19#include <mach/mach.h>
20#include <mach/task.h>
21#include <pthread.h>
22#include <signal.h>
23#include <spawn.h>
24#include <sys/fcntl.h>
25#include <sys/ptrace.h>
26#include <sys/stat.h>
27#include <sys/sysctl.h>
28#include <sys/types.h>
29#include <unistd.h>
30#include <uuid/uuid.h>
31
32#include <algorithm>
33#include <map>
34
35#import <Foundation/Foundation.h>
36
37#include "DNBDataRef.h"
38#include "DNBLog.h"
39#include "DNBThreadResumeActions.h"
40#include "DNBTimer.h"
41#include "MachProcess.h"
42#include "PseudoTerminal.h"
43
44#include "CFBundle.h"
45#include "CFString.h"
46
47#ifndef PLATFORM_BRIDGEOS
48#define PLATFORM_BRIDGEOS 5
49#endif
50
51#ifndef PLATFORM_MACCATALYST
52#define PLATFORM_MACCATALYST 6
53#endif
54
55#ifndef PLATFORM_IOSSIMULATOR
56#define PLATFORM_IOSSIMULATOR 7
57#endif
58
59#ifndef PLATFORM_TVOSSIMULATOR
60#define PLATFORM_TVOSSIMULATOR 8
61#endif
62
63#ifndef PLATFORM_WATCHOSSIMULATOR
64#define PLATFORM_WATCHOSSIMULATOR 9
65#endif
66
67#ifndef PLATFORM_DRIVERKIT
68#define PLATFORM_DRIVERKIT 10
69#endif
70
71#ifdef WITH_SPRINGBOARD
72
73#include <CoreFoundation/CoreFoundation.h>
74#include <SpringBoardServices/SBSWatchdogAssertion.h>
75#include <SpringBoardServices/SpringBoardServer.h>
76
77#endif // WITH_SPRINGBOARD
78
79#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS)
80// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
81// or NULL if there was some problem getting the bundle id.
82static CFStringRef CopyBundleIDForPath(const char *app_bundle_path,
83                                       DNBError &err_str);
84#endif
85
86#if defined(WITH_BKS) || defined(WITH_FBS)
87#import <Foundation/Foundation.h>
88static const int OPEN_APPLICATION_TIMEOUT_ERROR = 111;
89typedef void (*SetErrorFunction)(NSInteger, std::string, DNBError &);
90typedef bool (*CallOpenApplicationFunction)(NSString *bundleIDNSStr,
91                                            NSDictionary *options,
92                                            DNBError &error, pid_t *return_pid);
93// This function runs the BKSSystemService (or FBSSystemService) method
94// openApplication:options:clientPort:withResult,
95// messaging the app passed in bundleIDNSStr.
96// The function should be run inside of an NSAutoReleasePool.
97//
98// It will use the "options" dictionary passed in, and fill the error passed in
99// if there is an error.
100// If return_pid is not NULL, we'll fetch the pid that was made for the
101// bundleID.
102// If bundleIDNSStr is NULL, then the system application will be messaged.
103
104template <typename OpenFlavor, typename ErrorFlavor,
105          ErrorFlavor no_error_enum_value, SetErrorFunction error_function>
106static bool CallBoardSystemServiceOpenApplication(NSString *bundleIDNSStr,
107                                                  NSDictionary *options,
108                                                  DNBError &error,
109                                                  pid_t *return_pid) {
110  // Now make our systemService:
111  OpenFlavor *system_service = [[OpenFlavor alloc] init];
112
113  if (bundleIDNSStr == nil) {
114    bundleIDNSStr = [system_service systemApplicationBundleIdentifier];
115    if (bundleIDNSStr == nil) {
116      // Okay, no system app...
117      error.SetErrorString("No system application to message.");
118      return false;
119    }
120  }
121
122  mach_port_t client_port = [system_service createClientPort];
123  __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
124  __block ErrorFlavor open_app_error = no_error_enum_value;
125  __block std::string open_app_error_string;
126  bool wants_pid = (return_pid != NULL);
127  __block pid_t pid_in_block;
128
129  const char *cstr = [bundleIDNSStr UTF8String];
130  if (!cstr)
131    cstr = "<Unknown Bundle ID>";
132
133  NSString *description = [options description];
134  DNBLog("About to launch process for bundle ID: %s - options:\n%s", cstr,
135    [description UTF8String]);
136  [system_service
137      openApplication:bundleIDNSStr
138              options:options
139           clientPort:client_port
140           withResult:^(NSError *bks_error) {
141             // The system service will cleanup the client port we created for
142             // us.
143             if (bks_error)
144               open_app_error = (ErrorFlavor)[bks_error code];
145
146             if (open_app_error == no_error_enum_value) {
147               if (wants_pid) {
148                 pid_in_block =
149                     [system_service pidForApplication:bundleIDNSStr];
150                 DNBLog(
151                     "In completion handler, got pid for bundle id, pid: %d.",
152                     pid_in_block);
153                 DNBLogThreadedIf(
154                     LOG_PROCESS,
155                     "In completion handler, got pid for bundle id, pid: %d.",
156                     pid_in_block);
157               } else
158                 DNBLogThreadedIf(LOG_PROCESS,
159                                  "In completion handler: success.");
160             } else {
161               const char *error_str =
162                   [(NSString *)[bks_error localizedDescription] UTF8String];
163               if (error_str) {
164                 open_app_error_string = error_str;
165               }
166               DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send "
167                                             "event, got error \"%s\"(%ld).",
168                                error_str ? error_str : "<unknown error>",
169                                open_app_error);
170               // REMOVE ME
171               DNBLogError("In completion handler for send event, got error "
172                           "\"%s\"(%ld).",
173                           error_str ? error_str : "<unknown error>",
174                           open_app_error);
175             }
176
177             [system_service release];
178             dispatch_semaphore_signal(semaphore);
179           }
180
181  ];
182
183  const uint32_t timeout_secs = 30;
184
185  dispatch_time_t timeout =
186      dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
187
188  long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
189
190  dispatch_release(semaphore);
191
192  if (!success) {
193    DNBLogError("timed out trying to send openApplication to %s.", cstr);
194    error.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
195    error.SetErrorString("timed out trying to launch app");
196  } else if (open_app_error != no_error_enum_value) {
197    error_function(open_app_error, open_app_error_string, error);
198    DNBLogError("unable to launch the application with CFBundleIdentifier '%s' "
199                "bks_error = %u",
200                cstr, open_app_error);
201    success = false;
202  } else if (wants_pid) {
203    *return_pid = pid_in_block;
204    DNBLogThreadedIf(
205        LOG_PROCESS,
206        "Out of completion handler, pid from block %d and passing out: %d",
207        pid_in_block, *return_pid);
208  }
209
210  return success;
211}
212#endif
213
214#if defined(WITH_BKS) || defined(WITH_FBS)
215static void SplitEventData(const char *data, std::vector<std::string> &elements)
216{
217  elements.clear();
218  if (!data)
219    return;
220
221  const char *start = data;
222
223  while (*start != '\0') {
224    const char *token = strchr(start, ':');
225    if (!token) {
226      elements.push_back(std::string(start));
227      return;
228    }
229    if (token != start)
230      elements.push_back(std::string(start, token - start));
231    start = ++token;
232  }
233}
234#endif
235
236#ifdef WITH_BKS
237#import <Foundation/Foundation.h>
238extern "C" {
239#import <BackBoardServices/BKSOpenApplicationConstants_Private.h>
240#import <BackBoardServices/BKSSystemService_LaunchServices.h>
241#import <BackBoardServices/BackBoardServices.h>
242}
243
244static bool IsBKSProcess(nub_process_t pid) {
245  BKSApplicationStateMonitor *state_monitor =
246      [[BKSApplicationStateMonitor alloc] init];
247  BKSApplicationState app_state =
248      [state_monitor mostElevatedApplicationStateForPID:pid];
249  return app_state != BKSApplicationStateUnknown;
250}
251
252static void SetBKSError(NSInteger error_code,
253                        std::string error_description,
254                        DNBError &error) {
255  error.SetError(error_code, DNBError::BackBoard);
256  NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString(
257      (BKSOpenApplicationErrorCode)error_code);
258  std::string err_str = "unknown BKS error";
259  if (error_description.empty() == false) {
260    err_str = error_description;
261  } else if (err_nsstr != nullptr) {
262    err_str = [err_nsstr UTF8String];
263  }
264  error.SetErrorString(err_str.c_str());
265}
266
267static bool BKSAddEventDataToOptions(NSMutableDictionary *options,
268                                     const char *event_data,
269                                     DNBError &option_error) {
270  std::vector<std::string> values;
271  SplitEventData(event_data, values);
272  bool found_one = false;
273  for (std::string value : values)
274  {
275      if (value.compare("BackgroundContentFetching") == 0) {
276        DNBLog("Setting ActivateForEvent key in options dictionary.");
277        NSDictionary *event_details = [NSDictionary dictionary];
278        NSDictionary *event_dictionary = [NSDictionary
279            dictionaryWithObject:event_details
280                          forKey:
281                              BKSActivateForEventOptionTypeBackgroundContentFetching];
282        [options setObject:event_dictionary
283                    forKey:BKSOpenApplicationOptionKeyActivateForEvent];
284        found_one = true;
285      } else if (value.compare("ActivateSuspended") == 0) {
286        DNBLog("Setting ActivateSuspended key in options dictionary.");
287        [options setObject:@YES forKey: BKSOpenApplicationOptionKeyActivateSuspended];
288        found_one = true;
289      } else {
290        DNBLogError("Unrecognized event type: %s.  Ignoring.", value.c_str());
291        option_error.SetErrorString("Unrecognized event data");
292      }
293  }
294  return found_one;
295}
296
297static NSMutableDictionary *BKSCreateOptionsDictionary(
298    const char *app_bundle_path, NSMutableArray *launch_argv,
299    NSMutableDictionary *launch_envp, NSString *stdio_path, bool disable_aslr,
300    const char *event_data) {
301  NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
302  if (launch_argv != nil)
303    [debug_options setObject:launch_argv forKey:BKSDebugOptionKeyArguments];
304  if (launch_envp != nil)
305    [debug_options setObject:launch_envp forKey:BKSDebugOptionKeyEnvironment];
306
307  [debug_options setObject:stdio_path forKey:BKSDebugOptionKeyStandardOutPath];
308  [debug_options setObject:stdio_path
309                    forKey:BKSDebugOptionKeyStandardErrorPath];
310  [debug_options setObject:[NSNumber numberWithBool:YES]
311                    forKey:BKSDebugOptionKeyWaitForDebugger];
312  if (disable_aslr)
313    [debug_options setObject:[NSNumber numberWithBool:YES]
314                      forKey:BKSDebugOptionKeyDisableASLR];
315
316  // That will go in the overall dictionary:
317
318  NSMutableDictionary *options = [NSMutableDictionary dictionary];
319  [options setObject:debug_options
320              forKey:BKSOpenApplicationOptionKeyDebuggingOptions];
321  // And there are some other options at the top level in this dictionary:
322  [options setObject:[NSNumber numberWithBool:YES]
323              forKey:BKSOpenApplicationOptionKeyUnlockDevice];
324
325  DNBError error;
326  BKSAddEventDataToOptions(options, event_data, error);
327
328  return options;
329}
330
331static CallOpenApplicationFunction BKSCallOpenApplicationFunction =
332    CallBoardSystemServiceOpenApplication<
333        BKSSystemService, BKSOpenApplicationErrorCode,
334        BKSOpenApplicationErrorCodeNone, SetBKSError>;
335#endif // WITH_BKS
336
337#ifdef WITH_FBS
338#import <Foundation/Foundation.h>
339extern "C" {
340#import <FrontBoardServices/FBSOpenApplicationConstants_Private.h>
341#import <FrontBoardServices/FBSSystemService_LaunchServices.h>
342#import <FrontBoardServices/FrontBoardServices.h>
343#import <MobileCoreServices/LSResourceProxy.h>
344#import <MobileCoreServices/MobileCoreServices.h>
345}
346
347#ifdef WITH_BKS
348static bool IsFBSProcess(nub_process_t pid) {
349  BKSApplicationStateMonitor *state_monitor =
350      [[BKSApplicationStateMonitor alloc] init];
351  BKSApplicationState app_state =
352      [state_monitor mostElevatedApplicationStateForPID:pid];
353  return app_state != BKSApplicationStateUnknown;
354}
355#else
356static bool IsFBSProcess(nub_process_t pid) {
357  // FIXME: What is the FBS equivalent of BKSApplicationStateMonitor
358  return false;
359}
360#endif
361
362static void SetFBSError(NSInteger error_code,
363                        std::string error_description,
364                        DNBError &error) {
365  error.SetError((DNBError::ValueType)error_code, DNBError::FrontBoard);
366  NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString(
367      (FBSOpenApplicationErrorCode)error_code);
368  std::string err_str = "unknown FBS error";
369  if (error_description.empty() == false) {
370    err_str = error_description;
371  } else if (err_nsstr != nullptr) {
372    err_str = [err_nsstr UTF8String];
373  }
374  error.SetErrorString(err_str.c_str());
375}
376
377static bool FBSAddEventDataToOptions(NSMutableDictionary *options,
378                                     const char *event_data,
379                                     DNBError &option_error) {
380  std::vector<std::string> values;
381  SplitEventData(event_data, values);
382  bool found_one = false;
383  for (std::string value : values)
384  {
385      if (value.compare("BackgroundContentFetching") == 0) {
386        DNBLog("Setting ActivateForEvent key in options dictionary.");
387        NSDictionary *event_details = [NSDictionary dictionary];
388        NSDictionary *event_dictionary = [NSDictionary
389            dictionaryWithObject:event_details
390                          forKey:
391                              FBSActivateForEventOptionTypeBackgroundContentFetching];
392        [options setObject:event_dictionary
393                    forKey:FBSOpenApplicationOptionKeyActivateForEvent];
394        found_one = true;
395      } else if (value.compare("ActivateSuspended") == 0) {
396        DNBLog("Setting ActivateSuspended key in options dictionary.");
397        [options setObject:@YES forKey: FBSOpenApplicationOptionKeyActivateSuspended];
398        found_one = true;
399      } else {
400        DNBLogError("Unrecognized event type: %s.  Ignoring.", value.c_str());
401        option_error.SetErrorString("Unrecognized event data.");
402      }
403  }
404  return found_one;
405}
406
407static NSMutableDictionary *
408FBSCreateOptionsDictionary(const char *app_bundle_path,
409                           NSMutableArray *launch_argv,
410                           NSDictionary *launch_envp, NSString *stdio_path,
411                           bool disable_aslr, const char *event_data) {
412  NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
413
414  if (launch_argv != nil)
415    [debug_options setObject:launch_argv forKey:FBSDebugOptionKeyArguments];
416  if (launch_envp != nil)
417    [debug_options setObject:launch_envp forKey:FBSDebugOptionKeyEnvironment];
418
419  [debug_options setObject:stdio_path forKey:FBSDebugOptionKeyStandardOutPath];
420  [debug_options setObject:stdio_path
421                    forKey:FBSDebugOptionKeyStandardErrorPath];
422  [debug_options setObject:[NSNumber numberWithBool:YES]
423                    forKey:FBSDebugOptionKeyWaitForDebugger];
424  if (disable_aslr)
425    [debug_options setObject:[NSNumber numberWithBool:YES]
426                      forKey:FBSDebugOptionKeyDisableASLR];
427
428  // That will go in the overall dictionary:
429
430  NSMutableDictionary *options = [NSMutableDictionary dictionary];
431  [options setObject:debug_options
432              forKey:FBSOpenApplicationOptionKeyDebuggingOptions];
433  // And there are some other options at the top level in this dictionary:
434  [options setObject:[NSNumber numberWithBool:YES]
435              forKey:FBSOpenApplicationOptionKeyUnlockDevice];
436
437  // We have to get the "sequence ID & UUID" for this app bundle path and send
438  // them to FBS:
439
440  NSURL *app_bundle_url =
441      [NSURL fileURLWithPath:[NSString stringWithUTF8String:app_bundle_path]
442                 isDirectory:YES];
443  LSApplicationProxy *app_proxy =
444      [LSApplicationProxy applicationProxyForBundleURL:app_bundle_url];
445  if (app_proxy) {
446    DNBLog("Sending AppProxy info: sequence no: %lu, GUID: %s.",
447           app_proxy.sequenceNumber,
448           [app_proxy.cacheGUID.UUIDString UTF8String]);
449    [options
450        setObject:[NSNumber numberWithUnsignedInteger:app_proxy.sequenceNumber]
451           forKey:FBSOpenApplicationOptionKeyLSSequenceNumber];
452    [options setObject:app_proxy.cacheGUID.UUIDString
453                forKey:FBSOpenApplicationOptionKeyLSCacheGUID];
454  }
455
456  DNBError error;
457  FBSAddEventDataToOptions(options, event_data, error);
458
459  return options;
460}
461static CallOpenApplicationFunction FBSCallOpenApplicationFunction =
462    CallBoardSystemServiceOpenApplication<
463        FBSSystemService, FBSOpenApplicationErrorCode,
464        FBSOpenApplicationErrorCodeNone, SetFBSError>;
465#endif // WITH_FBS
466
467#if 0
468#define DEBUG_LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
469#else
470#define DEBUG_LOG(fmt, ...)
471#endif
472
473#ifndef MACH_PROCESS_USE_POSIX_SPAWN
474#define MACH_PROCESS_USE_POSIX_SPAWN 1
475#endif
476
477#ifndef _POSIX_SPAWN_DISABLE_ASLR
478#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
479#endif
480
481MachProcess::MachProcess()
482    : m_pid(0), m_cpu_type(0), m_child_stdin(-1), m_child_stdout(-1),
483      m_child_stderr(-1), m_path(), m_args(), m_task(this),
484      m_flags(eMachProcessFlagsNone), m_stdio_thread(0),
485      m_stdio_mutex(PTHREAD_MUTEX_RECURSIVE), m_stdout_data(),
486      m_profile_enabled(false), m_profile_interval_usec(0), m_profile_thread(0),
487      m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), m_profile_data(),
488      m_thread_actions(), m_exception_messages(),
489      m_exception_messages_mutex(PTHREAD_MUTEX_RECURSIVE), m_thread_list(),
490      m_activities(), m_state(eStateUnloaded),
491      m_state_mutex(PTHREAD_MUTEX_RECURSIVE), m_events(0, kAllEventsMask),
492      m_private_events(0, kAllEventsMask), m_breakpoints(), m_watchpoints(),
493      m_name_to_addr_callback(NULL), m_name_to_addr_baton(NULL),
494      m_image_infos_callback(NULL), m_image_infos_baton(NULL),
495      m_sent_interrupt_signo(0), m_auto_resume_signo(0), m_did_exec(false),
496      m_dyld_process_info_create(nullptr),
497      m_dyld_process_info_for_each_image(nullptr),
498      m_dyld_process_info_release(nullptr),
499      m_dyld_process_info_get_cache(nullptr) {
500  m_dyld_process_info_create =
501      (void *(*)(task_t task, uint64_t timestamp, kern_return_t * kernelError))
502          dlsym(RTLD_DEFAULT, "_dyld_process_info_create");
503  m_dyld_process_info_for_each_image =
504      (void (*)(void *info, void (^)(uint64_t machHeaderAddress,
505                                     const uuid_t uuid, const char *path)))
506          dlsym(RTLD_DEFAULT, "_dyld_process_info_for_each_image");
507  m_dyld_process_info_release =
508      (void (*)(void *info))dlsym(RTLD_DEFAULT, "_dyld_process_info_release");
509  m_dyld_process_info_get_cache = (void (*)(void *info, void *cacheInfo))dlsym(
510      RTLD_DEFAULT, "_dyld_process_info_get_cache");
511  m_dyld_process_info_get_platform = (uint32_t (*)(void *info))dlsym(
512      RTLD_DEFAULT, "_dyld_process_info_get_platform");
513
514  DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
515}
516
517MachProcess::~MachProcess() {
518  DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
519  Clear();
520}
521
522pid_t MachProcess::SetProcessID(pid_t pid) {
523  // Free any previous process specific data or resources
524  Clear();
525  // Set the current PID appropriately
526  if (pid == 0)
527    m_pid = ::getpid();
528  else
529    m_pid = pid;
530  return m_pid; // Return actually PID in case a zero pid was passed in
531}
532
533nub_state_t MachProcess::GetState() {
534  // If any other threads access this we will need a mutex for it
535  PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
536  return m_state;
537}
538
539const char *MachProcess::ThreadGetName(nub_thread_t tid) {
540  return m_thread_list.GetName(tid);
541}
542
543nub_state_t MachProcess::ThreadGetState(nub_thread_t tid) {
544  return m_thread_list.GetState(tid);
545}
546
547nub_size_t MachProcess::GetNumThreads() const {
548  return m_thread_list.NumThreads();
549}
550
551nub_thread_t MachProcess::GetThreadAtIndex(nub_size_t thread_idx) const {
552  return m_thread_list.ThreadIDAtIndex(thread_idx);
553}
554
555nub_thread_t
556MachProcess::GetThreadIDForMachPortNumber(thread_t mach_port_number) const {
557  return m_thread_list.GetThreadIDByMachPortNumber(mach_port_number);
558}
559
560nub_bool_t MachProcess::SyncThreadState(nub_thread_t tid) {
561  MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid));
562  if (!thread_sp)
563    return false;
564  kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber());
565  DNBLogThreadedIf(LOG_THREAD, "thread = 0x%8.8" PRIx32
566                               " calling thread_abort_safely (tid) => %u "
567                               "(GetGPRState() for stop_count = %u)",
568                   thread_sp->MachPortNumber(), kret,
569                   thread_sp->Process()->StopCount());
570
571  if (kret == KERN_SUCCESS)
572    return true;
573  else
574    return false;
575}
576
577ThreadInfo::QoS MachProcess::GetRequestedQoS(nub_thread_t tid, nub_addr_t tsd,
578                                             uint64_t dti_qos_class_index) {
579  return m_thread_list.GetRequestedQoS(tid, tsd, dti_qos_class_index);
580}
581
582nub_addr_t MachProcess::GetPThreadT(nub_thread_t tid) {
583  return m_thread_list.GetPThreadT(tid);
584}
585
586nub_addr_t MachProcess::GetDispatchQueueT(nub_thread_t tid) {
587  return m_thread_list.GetDispatchQueueT(tid);
588}
589
590nub_addr_t MachProcess::GetTSDAddressForThread(
591    nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset,
592    uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) {
593  return m_thread_list.GetTSDAddressForThread(
594      tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
595      plo_pthread_tsd_entry_size);
596}
597
598
599const char *MachProcess::GetDeploymentInfo(const struct load_command& lc,
600                                           uint64_t load_command_address,
601                                           uint32_t& major_version,
602                                           uint32_t& minor_version,
603                                           uint32_t& patch_version) {
604  uint32_t cmd = lc.cmd & ~LC_REQ_DYLD;
605  bool lc_cmd_known =
606    cmd == LC_VERSION_MIN_IPHONEOS || cmd == LC_VERSION_MIN_MACOSX ||
607    cmd == LC_VERSION_MIN_TVOS || cmd == LC_VERSION_MIN_WATCHOS;
608
609  if (lc_cmd_known) {
610    struct version_min_command vers_cmd;
611    if (ReadMemory(load_command_address, sizeof(struct version_min_command),
612                   &vers_cmd) != sizeof(struct version_min_command)) {
613      return nullptr;
614    }
615    major_version = vers_cmd.sdk >> 16;
616    minor_version = (vers_cmd.sdk >> 8) & 0xffu;
617    patch_version = vers_cmd.sdk & 0xffu;
618
619    switch (cmd) {
620    case LC_VERSION_MIN_IPHONEOS:
621      return "ios";
622    case LC_VERSION_MIN_MACOSX:
623      return "macosx";
624    case LC_VERSION_MIN_TVOS:
625      return "tvos";
626    case LC_VERSION_MIN_WATCHOS:
627      return "watchos";
628    default:
629      return nullptr;
630    }
631  }
632#if defined (LC_BUILD_VERSION)
633  if (cmd == LC_BUILD_VERSION) {
634    struct build_version_command build_vers;
635    if (ReadMemory(load_command_address, sizeof(struct build_version_command),
636                   &build_vers) != sizeof(struct build_version_command)) {
637      return nullptr;
638    }
639    major_version = build_vers.sdk >> 16;;
640    minor_version = (build_vers.sdk >> 8) & 0xffu;
641    patch_version = build_vers.sdk & 0xffu;
642
643    switch (build_vers.platform) {
644    case PLATFORM_MACOS:
645      return "macosx";
646    case PLATFORM_MACCATALYST:
647      return "maccatalyst";
648    case PLATFORM_IOS:
649    case PLATFORM_IOSSIMULATOR:
650      return "ios";
651    case PLATFORM_TVOS:
652    case PLATFORM_TVOSSIMULATOR:
653      return "tvos";
654    case PLATFORM_WATCHOS:
655    case PLATFORM_WATCHOSSIMULATOR:
656      return "watchos";
657    case PLATFORM_BRIDGEOS:
658      return "bridgeos";
659    case PLATFORM_DRIVERKIT:
660      return "driverkit";
661    }
662  }
663#endif
664  return nullptr;
665}
666
667// Given an address, read the mach-o header and load commands out of memory to
668// fill in
669// the mach_o_information "inf" object.
670//
671// Returns false if there was an error in reading this mach-o file header/load
672// commands.
673
674bool MachProcess::GetMachOInformationFromMemory(
675    uint32_t dyld_platform, nub_addr_t mach_o_header_addr, int wordsize,
676    struct mach_o_information &inf) {
677  uint64_t load_cmds_p;
678  if (wordsize == 4) {
679    struct mach_header header;
680    if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header), &header) !=
681        sizeof(struct mach_header)) {
682      return false;
683    }
684    load_cmds_p = mach_o_header_addr + sizeof(struct mach_header);
685    inf.mach_header.magic = header.magic;
686    inf.mach_header.cputype = header.cputype;
687    // high byte of cpusubtype is used for "capability bits", v.
688    // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h
689    inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff;
690    inf.mach_header.filetype = header.filetype;
691    inf.mach_header.ncmds = header.ncmds;
692    inf.mach_header.sizeofcmds = header.sizeofcmds;
693    inf.mach_header.flags = header.flags;
694  } else {
695    struct mach_header_64 header;
696    if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header_64),
697                   &header) != sizeof(struct mach_header_64)) {
698      return false;
699    }
700    load_cmds_p = mach_o_header_addr + sizeof(struct mach_header_64);
701    inf.mach_header.magic = header.magic;
702    inf.mach_header.cputype = header.cputype;
703    // high byte of cpusubtype is used for "capability bits", v.
704    // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h
705    inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff;
706    inf.mach_header.filetype = header.filetype;
707    inf.mach_header.ncmds = header.ncmds;
708    inf.mach_header.sizeofcmds = header.sizeofcmds;
709    inf.mach_header.flags = header.flags;
710  }
711  for (uint32_t j = 0; j < inf.mach_header.ncmds; j++) {
712    struct load_command lc;
713    if (ReadMemory(load_cmds_p, sizeof(struct load_command), &lc) !=
714        sizeof(struct load_command)) {
715      return false;
716    }
717    if (lc.cmd == LC_SEGMENT) {
718      struct segment_command seg;
719      if (ReadMemory(load_cmds_p, sizeof(struct segment_command), &seg) !=
720          sizeof(struct segment_command)) {
721        return false;
722      }
723      struct mach_o_segment this_seg;
724      char name[17];
725      ::memset(name, 0, sizeof(name));
726      memcpy(name, seg.segname, sizeof(seg.segname));
727      this_seg.name = name;
728      this_seg.vmaddr = seg.vmaddr;
729      this_seg.vmsize = seg.vmsize;
730      this_seg.fileoff = seg.fileoff;
731      this_seg.filesize = seg.filesize;
732      this_seg.maxprot = seg.maxprot;
733      this_seg.initprot = seg.initprot;
734      this_seg.nsects = seg.nsects;
735      this_seg.flags = seg.flags;
736      inf.segments.push_back(this_seg);
737      if (this_seg.name == "ExecExtraSuspend")
738        m_task.TaskWillExecProcessesSuspended();
739    }
740    if (lc.cmd == LC_SEGMENT_64) {
741      struct segment_command_64 seg;
742      if (ReadMemory(load_cmds_p, sizeof(struct segment_command_64), &seg) !=
743          sizeof(struct segment_command_64)) {
744        return false;
745      }
746      struct mach_o_segment this_seg;
747      char name[17];
748      ::memset(name, 0, sizeof(name));
749      memcpy(name, seg.segname, sizeof(seg.segname));
750      this_seg.name = name;
751      this_seg.vmaddr = seg.vmaddr;
752      this_seg.vmsize = seg.vmsize;
753      this_seg.fileoff = seg.fileoff;
754      this_seg.filesize = seg.filesize;
755      this_seg.maxprot = seg.maxprot;
756      this_seg.initprot = seg.initprot;
757      this_seg.nsects = seg.nsects;
758      this_seg.flags = seg.flags;
759      inf.segments.push_back(this_seg);
760      if (this_seg.name == "ExecExtraSuspend")
761        m_task.TaskWillExecProcessesSuspended();
762    }
763    if (lc.cmd == LC_UUID) {
764      struct uuid_command uuidcmd;
765      if (ReadMemory(load_cmds_p, sizeof(struct uuid_command), &uuidcmd) ==
766          sizeof(struct uuid_command))
767        uuid_copy(inf.uuid, uuidcmd.uuid);
768    }
769
770    uint32_t major_version, minor_version, patch_version;
771    if (const char *lc_platform = GetDeploymentInfo(
772            lc, load_cmds_p, major_version, minor_version, patch_version)) {
773      // macCatalyst support.
774      //
775      // This handles two special cases:
776      //
777      // 1. Frameworks that have both a PLATFORM_MACOS and a
778      //    PLATFORM_MACCATALYST load command.  Make sure to select
779      //    the requested one.
780      //
781      // 2. The xctest binary is a pure macOS binary but is launched
782      //    with DYLD_FORCE_PLATFORM=6.
783      if (dyld_platform == PLATFORM_MACCATALYST &&
784          inf.mach_header.filetype == MH_EXECUTE &&
785          inf.min_version_os_name.empty() &&
786          (strcmp("macosx", lc_platform) == 0)) {
787        // DYLD says this *is* a macCatalyst process. If we haven't
788        // parsed any load commands, transform a macOS load command
789        // into a generic macCatalyst load command. It will be
790        // overwritten by a more specific one if there is one.  This
791        // is only done for the main executable. It is perfectly fine
792        // for a macCatalyst binary to link against a macOS-only framework.
793        inf.min_version_os_name = "maccatalyst";
794        inf.min_version_os_version = GetMacCatalystVersionString();
795      } else if (dyld_platform != PLATFORM_MACCATALYST &&
796                 inf.min_version_os_name == "macosx") {
797        // This is a binary with both PLATFORM_MACOS and
798        // PLATFORM_MACCATALYST load commands and the process is not
799        // running as PLATFORM_MACCATALYST. Stick with the
800        // "macosx" load command that we've already processed,
801        // ignore this one, which is presumed to be a
802        // PLATFORM_MACCATALYST one.
803      } else {
804        inf.min_version_os_name = lc_platform;
805        inf.min_version_os_version = "";
806        inf.min_version_os_version += std::to_string(major_version);
807        inf.min_version_os_version += ".";
808        inf.min_version_os_version += std::to_string(minor_version);
809        if (patch_version != 0) {
810          inf.min_version_os_version += ".";
811          inf.min_version_os_version += std::to_string(patch_version);
812        }
813      }
814    }
815
816    load_cmds_p += lc.cmdsize;
817  }
818  return true;
819}
820
821// Given completely filled in array of binary_image_information structures,
822// create a JSONGenerator object
823// with all the details we want to send to lldb.
824JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON(
825    const std::vector<struct binary_image_information> &image_infos) {
826
827  JSONGenerator::ArraySP image_infos_array_sp(new JSONGenerator::Array());
828
829  const size_t image_count = image_infos.size();
830
831  for (size_t i = 0; i < image_count; i++) {
832    JSONGenerator::DictionarySP image_info_dict_sp(
833        new JSONGenerator::Dictionary());
834    image_info_dict_sp->AddIntegerItem("load_address",
835                                       image_infos[i].load_address);
836    image_info_dict_sp->AddIntegerItem("mod_date", image_infos[i].mod_date);
837    image_info_dict_sp->AddStringItem("pathname", image_infos[i].filename);
838
839    uuid_string_t uuidstr;
840    uuid_unparse_upper(image_infos[i].macho_info.uuid, uuidstr);
841    image_info_dict_sp->AddStringItem("uuid", uuidstr);
842
843    if (!image_infos[i].macho_info.min_version_os_name.empty() &&
844        !image_infos[i].macho_info.min_version_os_version.empty()) {
845      image_info_dict_sp->AddStringItem(
846          "min_version_os_name", image_infos[i].macho_info.min_version_os_name);
847      image_info_dict_sp->AddStringItem(
848          "min_version_os_sdk",
849          image_infos[i].macho_info.min_version_os_version);
850    }
851
852    JSONGenerator::DictionarySP mach_header_dict_sp(
853        new JSONGenerator::Dictionary());
854    mach_header_dict_sp->AddIntegerItem(
855        "magic", image_infos[i].macho_info.mach_header.magic);
856    mach_header_dict_sp->AddIntegerItem(
857        "cputype", (uint32_t)image_infos[i].macho_info.mach_header.cputype);
858    mach_header_dict_sp->AddIntegerItem(
859        "cpusubtype",
860        (uint32_t)image_infos[i].macho_info.mach_header.cpusubtype);
861    mach_header_dict_sp->AddIntegerItem(
862        "filetype", image_infos[i].macho_info.mach_header.filetype);
863    mach_header_dict_sp->AddIntegerItem ("flags",
864                         image_infos[i].macho_info.mach_header.flags);
865
866    //          DynamicLoaderMacOSX doesn't currently need these fields, so
867    //          don't send them.
868    //            mach_header_dict_sp->AddIntegerItem ("ncmds",
869    //            image_infos[i].macho_info.mach_header.ncmds);
870    //            mach_header_dict_sp->AddIntegerItem ("sizeofcmds",
871    //            image_infos[i].macho_info.mach_header.sizeofcmds);
872    image_info_dict_sp->AddItem("mach_header", mach_header_dict_sp);
873
874    JSONGenerator::ArraySP segments_sp(new JSONGenerator::Array());
875    for (size_t j = 0; j < image_infos[i].macho_info.segments.size(); j++) {
876      JSONGenerator::DictionarySP segment_sp(new JSONGenerator::Dictionary());
877      segment_sp->AddStringItem("name",
878                                image_infos[i].macho_info.segments[j].name);
879      segment_sp->AddIntegerItem("vmaddr",
880                                 image_infos[i].macho_info.segments[j].vmaddr);
881      segment_sp->AddIntegerItem("vmsize",
882                                 image_infos[i].macho_info.segments[j].vmsize);
883      segment_sp->AddIntegerItem("fileoff",
884                                 image_infos[i].macho_info.segments[j].fileoff);
885      segment_sp->AddIntegerItem(
886          "filesize", image_infos[i].macho_info.segments[j].filesize);
887      segment_sp->AddIntegerItem("maxprot",
888                                 image_infos[i].macho_info.segments[j].maxprot);
889
890      //              DynamicLoaderMacOSX doesn't currently need these fields,
891      //              so don't send them.
892      //                segment_sp->AddIntegerItem ("initprot",
893      //                image_infos[i].macho_info.segments[j].initprot);
894      //                segment_sp->AddIntegerItem ("nsects",
895      //                image_infos[i].macho_info.segments[j].nsects);
896      //                segment_sp->AddIntegerItem ("flags",
897      //                image_infos[i].macho_info.segments[j].flags);
898      segments_sp->AddItem(segment_sp);
899    }
900    image_info_dict_sp->AddItem("segments", segments_sp);
901
902    image_infos_array_sp->AddItem(image_info_dict_sp);
903  }
904
905  JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary());
906  ;
907  reply_sp->AddItem("images", image_infos_array_sp);
908
909  return reply_sp;
910}
911
912// Get the shared library information using the old (pre-macOS 10.12, pre-iOS
913// 10, pre-tvOS 10, pre-watchOS 3)
914// code path.  We'll be given the address of an array of structures in the form
915// {void* load_addr, void* mod_date, void* pathname}
916//
917// In macOS 10.12 etc and newer, we'll use SPI calls into dyld to gather this
918// information.
919JSONGenerator::ObjectSP MachProcess::GetLoadedDynamicLibrariesInfos(
920    nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
921  JSONGenerator::DictionarySP reply_sp;
922
923  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
924  struct kinfo_proc processInfo;
925  size_t bufsize = sizeof(processInfo);
926  if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, &bufsize,
927             NULL, 0) == 0 &&
928      bufsize > 0) {
929    uint32_t pointer_size = 4;
930    if (processInfo.kp_proc.p_flag & P_LP64)
931      pointer_size = 8;
932
933    std::vector<struct binary_image_information> image_infos;
934    size_t image_infos_size = image_count * 3 * pointer_size;
935
936    uint8_t *image_info_buf = (uint8_t *)malloc(image_infos_size);
937    if (image_info_buf == NULL) {
938      return reply_sp;
939    }
940    if (ReadMemory(image_list_address, image_infos_size, image_info_buf) !=
941        image_infos_size) {
942      return reply_sp;
943    }
944
945    ////  First the image_infos array with (load addr, pathname, mod date)
946    ///tuples
947
948    for (size_t i = 0; i < image_count; i++) {
949      struct binary_image_information info;
950      nub_addr_t pathname_address;
951      if (pointer_size == 4) {
952        uint32_t load_address_32;
953        uint32_t pathname_address_32;
954        uint32_t mod_date_32;
955        ::memcpy(&load_address_32, image_info_buf + (i * 3 * pointer_size), 4);
956        ::memcpy(&pathname_address_32,
957                 image_info_buf + (i * 3 * pointer_size) + pointer_size, 4);
958        ::memcpy(&mod_date_32, image_info_buf + (i * 3 * pointer_size) +
959                                   pointer_size + pointer_size,
960                 4);
961        info.load_address = load_address_32;
962        info.mod_date = mod_date_32;
963        pathname_address = pathname_address_32;
964      } else {
965        uint64_t load_address_64;
966        uint64_t pathname_address_64;
967        uint64_t mod_date_64;
968        ::memcpy(&load_address_64, image_info_buf + (i * 3 * pointer_size), 8);
969        ::memcpy(&pathname_address_64,
970                 image_info_buf + (i * 3 * pointer_size) + pointer_size, 8);
971        ::memcpy(&mod_date_64, image_info_buf + (i * 3 * pointer_size) +
972                                   pointer_size + pointer_size,
973                 8);
974        info.load_address = load_address_64;
975        info.mod_date = mod_date_64;
976        pathname_address = pathname_address_64;
977      }
978      char strbuf[17];
979      info.filename = "";
980      uint64_t pathname_ptr = pathname_address;
981      bool still_reading = true;
982      while (still_reading &&
983             ReadMemory(pathname_ptr, sizeof(strbuf) - 1, strbuf) ==
984                 sizeof(strbuf) - 1) {
985        strbuf[sizeof(strbuf) - 1] = '\0';
986        info.filename += strbuf;
987        pathname_ptr += sizeof(strbuf) - 1;
988        // Stop if we found nul byte indicating the end of the string
989        for (size_t i = 0; i < sizeof(strbuf) - 1; i++) {
990          if (strbuf[i] == '\0') {
991            still_reading = false;
992            break;
993          }
994        }
995      }
996      uuid_clear(info.macho_info.uuid);
997      image_infos.push_back(info);
998    }
999    if (image_infos.size() == 0) {
1000      return reply_sp;
1001    }
1002
1003    free(image_info_buf);
1004
1005    ////  Second, read the mach header / load commands for all the dylibs
1006
1007    for (size_t i = 0; i < image_count; i++) {
1008      // The SPI to provide platform is not available on older systems.
1009      uint32_t platform = 0;
1010      if (!GetMachOInformationFromMemory(platform,
1011                                         image_infos[i].load_address,
1012                                         pointer_size,
1013                                         image_infos[i].macho_info)) {
1014        return reply_sp;
1015      }
1016    }
1017
1018    ////  Third, format all of the above in the JSONGenerator object.
1019
1020    return FormatDynamicLibrariesIntoJSON(image_infos);
1021  }
1022
1023  return reply_sp;
1024}
1025
1026// From dyld SPI header dyld_process_info.h
1027typedef void *dyld_process_info;
1028struct dyld_process_cache_info {
1029  uuid_t cacheUUID;          // UUID of cache used by process
1030  uint64_t cacheBaseAddress; // load address of dyld shared cache
1031  bool noCache;              // process is running without a dyld cache
1032  bool privateCache; // process is using a private copy of its dyld cache
1033};
1034
1035// Use the dyld SPI present in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer
1036// to get
1037// the load address, uuid, and filenames of all the libraries.
1038// This only fills in those three fields in the 'struct
1039// binary_image_information' - call
1040// GetMachOInformationFromMemory to fill in the mach-o header/load command
1041// details.
1042uint32_t MachProcess::GetAllLoadedBinariesViaDYLDSPI(
1043    std::vector<struct binary_image_information> &image_infos) {
1044  uint32_t platform = 0;
1045  kern_return_t kern_ret;
1046  if (m_dyld_process_info_create) {
1047    dyld_process_info info =
1048        m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret);
1049    if (info) {
1050      m_dyld_process_info_for_each_image(
1051          info,
1052          ^(uint64_t mach_header_addr, const uuid_t uuid, const char *path) {
1053            struct binary_image_information image;
1054            image.filename = path;
1055            uuid_copy(image.macho_info.uuid, uuid);
1056            image.load_address = mach_header_addr;
1057            image_infos.push_back(image);
1058          });
1059      if (m_dyld_process_info_get_platform)
1060        platform = m_dyld_process_info_get_platform(info);
1061      m_dyld_process_info_release(info);
1062    }
1063  }
1064  return platform;
1065}
1066
1067// Fetch information about all shared libraries using the dyld SPIs that exist
1068// in
1069// macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer.
1070JSONGenerator::ObjectSP
1071MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid) {
1072  JSONGenerator::DictionarySP reply_sp;
1073
1074  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
1075  struct kinfo_proc processInfo;
1076  size_t bufsize = sizeof(processInfo);
1077  if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, &bufsize,
1078             NULL, 0) == 0 &&
1079      bufsize > 0) {
1080    uint32_t pointer_size = 4;
1081    if (processInfo.kp_proc.p_flag & P_LP64)
1082      pointer_size = 8;
1083
1084    std::vector<struct binary_image_information> image_infos;
1085    uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(image_infos);
1086    const size_t image_count = image_infos.size();
1087    for (size_t i = 0; i < image_count; i++) {
1088      GetMachOInformationFromMemory(platform,
1089                                    image_infos[i].load_address, pointer_size,
1090                                    image_infos[i].macho_info);
1091    }
1092    return FormatDynamicLibrariesIntoJSON(image_infos);
1093  }
1094  return reply_sp;
1095}
1096
1097// Fetch information about the shared libraries at the given load addresses
1098// using the
1099// dyld SPIs that exist in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer.
1100JSONGenerator::ObjectSP MachProcess::GetLibrariesInfoForAddresses(
1101    nub_process_t pid, std::vector<uint64_t> &macho_addresses) {
1102  JSONGenerator::DictionarySP reply_sp;
1103
1104  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
1105  struct kinfo_proc processInfo;
1106  size_t bufsize = sizeof(processInfo);
1107  if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, &bufsize,
1108             NULL, 0) == 0 &&
1109      bufsize > 0) {
1110    uint32_t pointer_size = 4;
1111    if (processInfo.kp_proc.p_flag & P_LP64)
1112      pointer_size = 8;
1113
1114    std::vector<struct binary_image_information> all_image_infos;
1115    uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(all_image_infos);
1116
1117    std::vector<struct binary_image_information> image_infos;
1118    const size_t macho_addresses_count = macho_addresses.size();
1119    const size_t all_image_infos_count = all_image_infos.size();
1120    for (size_t i = 0; i < macho_addresses_count; i++) {
1121      for (size_t j = 0; j < all_image_infos_count; j++) {
1122        if (all_image_infos[j].load_address == macho_addresses[i]) {
1123          image_infos.push_back(all_image_infos[j]);
1124        }
1125      }
1126    }
1127
1128    const size_t image_infos_count = image_infos.size();
1129    for (size_t i = 0; i < image_infos_count; i++) {
1130      GetMachOInformationFromMemory(platform,
1131                                    image_infos[i].load_address, pointer_size,
1132                                    image_infos[i].macho_info);
1133    }
1134    return FormatDynamicLibrariesIntoJSON(image_infos);
1135  }
1136  return reply_sp;
1137}
1138
1139// From dyld's internal podyld_process_info.h:
1140
1141JSONGenerator::ObjectSP MachProcess::GetSharedCacheInfo(nub_process_t pid) {
1142  JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary());
1143  ;
1144  kern_return_t kern_ret;
1145  if (m_dyld_process_info_create && m_dyld_process_info_get_cache) {
1146    dyld_process_info info =
1147        m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret);
1148    if (info) {
1149      struct dyld_process_cache_info shared_cache_info;
1150      m_dyld_process_info_get_cache(info, &shared_cache_info);
1151
1152      reply_sp->AddIntegerItem("shared_cache_base_address",
1153                               shared_cache_info.cacheBaseAddress);
1154
1155      uuid_string_t uuidstr;
1156      uuid_unparse_upper(shared_cache_info.cacheUUID, uuidstr);
1157      reply_sp->AddStringItem("shared_cache_uuid", uuidstr);
1158
1159      reply_sp->AddBooleanItem("no_shared_cache", shared_cache_info.noCache);
1160      reply_sp->AddBooleanItem("shared_cache_private_cache",
1161                               shared_cache_info.privateCache);
1162
1163      m_dyld_process_info_release(info);
1164    }
1165  }
1166  return reply_sp;
1167}
1168
1169nub_thread_t MachProcess::GetCurrentThread() {
1170  return m_thread_list.CurrentThreadID();
1171}
1172
1173nub_thread_t MachProcess::GetCurrentThreadMachPort() {
1174  return m_thread_list.GetMachPortNumberByThreadID(
1175      m_thread_list.CurrentThreadID());
1176}
1177
1178nub_thread_t MachProcess::SetCurrentThread(nub_thread_t tid) {
1179  return m_thread_list.SetCurrentThread(tid);
1180}
1181
1182bool MachProcess::GetThreadStoppedReason(nub_thread_t tid,
1183                                         struct DNBThreadStopInfo *stop_info) {
1184  if (m_thread_list.GetThreadStoppedReason(tid, stop_info)) {
1185    if (m_did_exec)
1186      stop_info->reason = eStopTypeExec;
1187    return true;
1188  }
1189  return false;
1190}
1191
1192void MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const {
1193  return m_thread_list.DumpThreadStoppedReason(tid);
1194}
1195
1196const char *MachProcess::GetThreadInfo(nub_thread_t tid) const {
1197  return m_thread_list.GetThreadInfo(tid);
1198}
1199
1200uint32_t MachProcess::GetCPUType() {
1201  if (m_cpu_type == 0 && m_pid != 0)
1202    m_cpu_type = MachProcess::GetCPUTypeForLocalProcess(m_pid);
1203  return m_cpu_type;
1204}
1205
1206const DNBRegisterSetInfo *
1207MachProcess::GetRegisterSetInfo(nub_thread_t tid,
1208                                nub_size_t *num_reg_sets) const {
1209  MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid));
1210  if (thread_sp) {
1211    DNBArchProtocol *arch = thread_sp->GetArchProtocol();
1212    if (arch)
1213      return arch->GetRegisterSetInfo(num_reg_sets);
1214  }
1215  *num_reg_sets = 0;
1216  return NULL;
1217}
1218
1219bool MachProcess::GetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
1220                                   DNBRegisterValue *value) const {
1221  return m_thread_list.GetRegisterValue(tid, set, reg, value);
1222}
1223
1224bool MachProcess::SetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
1225                                   const DNBRegisterValue *value) const {
1226  return m_thread_list.SetRegisterValue(tid, set, reg, value);
1227}
1228
1229void MachProcess::SetState(nub_state_t new_state) {
1230  // If any other threads access this we will need a mutex for it
1231  uint32_t event_mask = 0;
1232
1233  // Scope for mutex locker
1234  {
1235    PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
1236    const nub_state_t old_state = m_state;
1237
1238    if (old_state == eStateExited) {
1239      DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new "
1240                                    "state since current state is exited",
1241                       DNBStateAsString(new_state));
1242    } else if (old_state == new_state) {
1243      DNBLogThreadedIf(
1244          LOG_PROCESS,
1245          "MachProcess::SetState(%s) ignoring redundant state change...",
1246          DNBStateAsString(new_state));
1247    } else {
1248      if (NUB_STATE_IS_STOPPED(new_state))
1249        event_mask = eEventProcessStoppedStateChanged;
1250      else
1251        event_mask = eEventProcessRunningStateChanged;
1252
1253      DNBLogThreadedIf(
1254          LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous "
1255                       "state was %s), event_mask = 0x%8.8x",
1256          DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask);
1257
1258      m_state = new_state;
1259      if (new_state == eStateStopped)
1260        m_stop_count++;
1261    }
1262  }
1263
1264  if (event_mask != 0) {
1265    m_events.SetEvents(event_mask);
1266    m_private_events.SetEvents(event_mask);
1267    if (event_mask == eEventProcessStoppedStateChanged)
1268      m_private_events.ResetEvents(eEventProcessRunningStateChanged);
1269    else
1270      m_private_events.ResetEvents(eEventProcessStoppedStateChanged);
1271
1272    // Wait for the event bit to reset if a reset ACK is requested
1273    m_events.WaitForResetAck(event_mask);
1274  }
1275}
1276
1277void MachProcess::Clear(bool detaching) {
1278  // Clear any cached thread list while the pid and task are still valid
1279
1280  m_task.Clear();
1281  // Now clear out all member variables
1282  m_pid = INVALID_NUB_PROCESS;
1283  if (!detaching)
1284    CloseChildFileDescriptors();
1285
1286  m_path.clear();
1287  m_args.clear();
1288  SetState(eStateUnloaded);
1289  m_flags = eMachProcessFlagsNone;
1290  m_stop_count = 0;
1291  m_thread_list.Clear();
1292  {
1293    PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
1294    m_exception_messages.clear();
1295  }
1296  m_activities.Clear();
1297  if (m_profile_thread) {
1298    pthread_join(m_profile_thread, NULL);
1299    m_profile_thread = NULL;
1300  }
1301}
1302
1303bool MachProcess::StartSTDIOThread() {
1304  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
1305  // Create the thread that watches for the child STDIO
1306  return ::pthread_create(&m_stdio_thread, NULL, MachProcess::STDIOThread,
1307                          this) == 0;
1308}
1309
1310void MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec,
1311                                          DNBProfileDataScanType scan_type) {
1312  m_profile_enabled = enable;
1313  m_profile_interval_usec = static_cast<useconds_t>(interval_usec);
1314  m_profile_scan_type = scan_type;
1315
1316  if (m_profile_enabled && (m_profile_thread == NULL)) {
1317    StartProfileThread();
1318  } else if (!m_profile_enabled && m_profile_thread) {
1319    pthread_join(m_profile_thread, NULL);
1320    m_profile_thread = NULL;
1321  }
1322}
1323
1324bool MachProcess::StartProfileThread() {
1325  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
1326  // Create the thread that profiles the inferior and reports back if enabled
1327  return ::pthread_create(&m_profile_thread, NULL, MachProcess::ProfileThread,
1328                          this) == 0;
1329}
1330
1331nub_addr_t MachProcess::LookupSymbol(const char *name, const char *shlib) {
1332  if (m_name_to_addr_callback != NULL && name && name[0])
1333    return m_name_to_addr_callback(ProcessID(), name, shlib,
1334                                   m_name_to_addr_baton);
1335  return INVALID_NUB_ADDRESS;
1336}
1337
1338bool MachProcess::Resume(const DNBThreadResumeActions &thread_actions) {
1339  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
1340  nub_state_t state = GetState();
1341
1342  if (CanResume(state)) {
1343    m_thread_actions = thread_actions;
1344    PrivateResume();
1345    return true;
1346  } else if (state == eStateRunning) {
1347    DNBLog("Resume() - task 0x%x is already running, ignoring...",
1348           m_task.TaskPort());
1349    return true;
1350  }
1351  DNBLog("Resume() - task 0x%x has state %s, can't continue...",
1352         m_task.TaskPort(), DNBStateAsString(state));
1353  return false;
1354}
1355
1356bool MachProcess::Kill(const struct timespec *timeout_abstime) {
1357  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()");
1358  nub_state_t state = DoSIGSTOP(true, false, NULL);
1359  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s",
1360                   DNBStateAsString(state));
1361  errno = 0;
1362  DNBLog("Sending ptrace PT_KILL to terminate inferior process.");
1363  ::ptrace(PT_KILL, m_pid, 0, 0);
1364  DNBError err;
1365  err.SetErrorToErrno();
1366  if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail()) {
1367    err.LogThreaded("MachProcess::Kill() DoSIGSTOP() ::ptrace "
1368            "(PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)",
1369            m_pid, err.Status(), err.AsString());
1370  }
1371  m_thread_actions = DNBThreadResumeActions(eStateRunning, 0);
1372  PrivateResume();
1373
1374  // Try and reap the process without touching our m_events since
1375  // we want the code above this to still get the eStateExited event
1376  const uint32_t reap_timeout_usec =
1377      1000000; // Wait 1 second and try to reap the process
1378  const uint32_t reap_interval_usec = 10000; //
1379  uint32_t reap_time_elapsed;
1380  for (reap_time_elapsed = 0; reap_time_elapsed < reap_timeout_usec;
1381       reap_time_elapsed += reap_interval_usec) {
1382    if (GetState() == eStateExited)
1383      break;
1384    usleep(reap_interval_usec);
1385  }
1386  DNBLog("Waited %u ms for process to be reaped (state = %s)",
1387         reap_time_elapsed / 1000, DNBStateAsString(GetState()));
1388  return true;
1389}
1390
1391bool MachProcess::Interrupt() {
1392  nub_state_t state = GetState();
1393  if (IsRunning(state)) {
1394    if (m_sent_interrupt_signo == 0) {
1395      m_sent_interrupt_signo = SIGSTOP;
1396      if (Signal(m_sent_interrupt_signo)) {
1397        DNBLogThreadedIf(
1398            LOG_PROCESS,
1399            "MachProcess::Interrupt() - sent %i signal to interrupt process",
1400            m_sent_interrupt_signo);
1401        return true;
1402      } else {
1403        m_sent_interrupt_signo = 0;
1404        DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to "
1405                                      "send %i signal to interrupt process",
1406                         m_sent_interrupt_signo);
1407      }
1408    } else {
1409      DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously "
1410                                    "sent an interrupt signal %i that hasn't "
1411                                    "been received yet, interrupt aborted",
1412                       m_sent_interrupt_signo);
1413    }
1414  } else {
1415    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already "
1416                                  "stopped, no interrupt sent");
1417  }
1418  return false;
1419}
1420
1421bool MachProcess::Signal(int signal, const struct timespec *timeout_abstime) {
1422  DNBLogThreadedIf(LOG_PROCESS,
1423                   "MachProcess::Signal (signal = %d, timeout = %p)", signal,
1424                   static_cast<const void *>(timeout_abstime));
1425  nub_state_t state = GetState();
1426  if (::kill(ProcessID(), signal) == 0) {
1427    // If we were running and we have a timeout, wait for the signal to stop
1428    if (IsRunning(state) && timeout_abstime) {
1429      DNBLogThreadedIf(LOG_PROCESS,
1430                       "MachProcess::Signal (signal = %d, timeout "
1431                       "= %p) waiting for signal to stop "
1432                       "process...",
1433                       signal, static_cast<const void *>(timeout_abstime));
1434      m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged,
1435                                        timeout_abstime);
1436      state = GetState();
1437      DNBLogThreadedIf(
1438          LOG_PROCESS,
1439          "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal,
1440          static_cast<const void *>(timeout_abstime), DNBStateAsString(state));
1441      return !IsRunning(state);
1442    }
1443    DNBLogThreadedIf(
1444        LOG_PROCESS,
1445        "MachProcess::Signal (signal = %d, timeout = %p) not waiting...",
1446        signal, static_cast<const void *>(timeout_abstime));
1447    return true;
1448  }
1449  DNBError err(errno, DNBError::POSIX);
1450  err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal);
1451  return false;
1452}
1453
1454bool MachProcess::SendEvent(const char *event, DNBError &send_err) {
1455  DNBLogThreadedIf(LOG_PROCESS,
1456                   "MachProcess::SendEvent (event = %s) to pid: %d", event,
1457                   m_pid);
1458  if (m_pid == INVALID_NUB_PROCESS)
1459    return false;
1460// FIXME: Shouldn't we use the launch flavor we were started with?
1461#if defined(WITH_FBS) || defined(WITH_BKS)
1462  return BoardServiceSendEvent(event, send_err);
1463#endif
1464  return true;
1465}
1466
1467nub_state_t MachProcess::DoSIGSTOP(bool clear_bps_and_wps, bool allow_running,
1468                                   uint32_t *thread_idx_ptr) {
1469  nub_state_t state = GetState();
1470  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s",
1471                   DNBStateAsString(state));
1472
1473  if (!IsRunning(state)) {
1474    if (clear_bps_and_wps) {
1475      DisableAllBreakpoints(true);
1476      DisableAllWatchpoints(true);
1477      clear_bps_and_wps = false;
1478    }
1479
1480    // If we already have a thread stopped due to a SIGSTOP, we don't have
1481    // to do anything...
1482    uint32_t thread_idx =
1483        m_thread_list.GetThreadIndexForThreadStoppedWithSignal(SIGSTOP);
1484    if (thread_idx_ptr)
1485      *thread_idx_ptr = thread_idx;
1486    if (thread_idx != UINT32_MAX)
1487      return GetState();
1488
1489    // No threads were stopped with a SIGSTOP, we need to run and halt the
1490    // process with a signal
1491    DNBLogThreadedIf(LOG_PROCESS,
1492                     "MachProcess::DoSIGSTOP() state = %s -- resuming process",
1493                     DNBStateAsString(state));
1494    if (allow_running)
1495      m_thread_actions = DNBThreadResumeActions(eStateRunning, 0);
1496    else
1497      m_thread_actions = DNBThreadResumeActions(eStateSuspended, 0);
1498
1499    PrivateResume();
1500
1501    // Reset the event that says we were indeed running
1502    m_events.ResetEvents(eEventProcessRunningStateChanged);
1503    state = GetState();
1504  }
1505
1506  // We need to be stopped in order to be able to detach, so we need
1507  // to send ourselves a SIGSTOP
1508
1509  DNBLogThreadedIf(LOG_PROCESS,
1510                   "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP",
1511                   DNBStateAsString(state));
1512  struct timespec sigstop_timeout;
1513  DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0);
1514  Signal(SIGSTOP, &sigstop_timeout);
1515  if (clear_bps_and_wps) {
1516    DisableAllBreakpoints(true);
1517    DisableAllWatchpoints(true);
1518    // clear_bps_and_wps = false;
1519  }
1520  uint32_t thread_idx =
1521      m_thread_list.GetThreadIndexForThreadStoppedWithSignal(SIGSTOP);
1522  if (thread_idx_ptr)
1523    *thread_idx_ptr = thread_idx;
1524  return GetState();
1525}
1526
1527bool MachProcess::Detach() {
1528  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()");
1529
1530  uint32_t thread_idx = UINT32_MAX;
1531  nub_state_t state = DoSIGSTOP(true, true, &thread_idx);
1532  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s",
1533                   DNBStateAsString(state));
1534
1535  {
1536    m_thread_actions.Clear();
1537    m_activities.Clear();
1538    DNBThreadResumeAction thread_action;
1539    thread_action.tid = m_thread_list.ThreadIDAtIndex(thread_idx);
1540    thread_action.state = eStateRunning;
1541    thread_action.signal = -1;
1542    thread_action.addr = INVALID_NUB_ADDRESS;
1543
1544    m_thread_actions.Append(thread_action);
1545    m_thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
1546
1547    PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
1548
1549    ReplyToAllExceptions();
1550  }
1551
1552  m_task.ShutDownExcecptionThread();
1553
1554  // Detach from our process
1555  errno = 0;
1556  nub_process_t pid = m_pid;
1557  int ret = ::ptrace(PT_DETACH, pid, (caddr_t)1, 0);
1558  DNBError err(errno, DNBError::POSIX);
1559  if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0))
1560    err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
1561
1562  // Resume our task
1563  m_task.Resume();
1564
1565  // NULL our task out as we have already restored all exception ports
1566  m_task.Clear();
1567
1568  // Clear out any notion of the process we once were
1569  const bool detaching = true;
1570  Clear(detaching);
1571
1572  SetState(eStateDetached);
1573
1574  return true;
1575}
1576
1577//----------------------------------------------------------------------
1578// ReadMemory from the MachProcess level will always remove any software
1579// breakpoints from the memory buffer before returning. If you wish to
1580// read memory and see those traps, read from the MachTask
1581// (m_task.ReadMemory()) as that version will give you what is actually
1582// in inferior memory.
1583//----------------------------------------------------------------------
1584nub_size_t MachProcess::ReadMemory(nub_addr_t addr, nub_size_t size,
1585                                   void *buf) {
1586  // We need to remove any current software traps (enabled software
1587  // breakpoints) that we may have placed in our tasks memory.
1588
1589  // First just read the memory as is
1590  nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf);
1591
1592  // Then place any opcodes that fall into this range back into the buffer
1593  // before we return this to callers.
1594  if (bytes_read > 0)
1595    m_breakpoints.RemoveTrapsFromBuffer(addr, bytes_read, buf);
1596  return bytes_read;
1597}
1598
1599//----------------------------------------------------------------------
1600// WriteMemory from the MachProcess level will always write memory around
1601// any software breakpoints. Any software breakpoints will have their
1602// opcodes modified if they are enabled. Any memory that doesn't overlap
1603// with software breakpoints will be written to. If you wish to write to
1604// inferior memory without this interference, then write to the MachTask
1605// (m_task.WriteMemory()) as that version will always modify inferior
1606// memory.
1607//----------------------------------------------------------------------
1608nub_size_t MachProcess::WriteMemory(nub_addr_t addr, nub_size_t size,
1609                                    const void *buf) {
1610  // We need to write any data that would go where any current software traps
1611  // (enabled software breakpoints) any software traps (breakpoints) that we
1612  // may have placed in our tasks memory.
1613
1614  std::vector<DNBBreakpoint *> bps;
1615
1616  const size_t num_bps =
1617      m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps);
1618  if (num_bps == 0)
1619    return m_task.WriteMemory(addr, size, buf);
1620
1621  nub_size_t bytes_written = 0;
1622  nub_addr_t intersect_addr;
1623  nub_size_t intersect_size;
1624  nub_size_t opcode_offset;
1625  const uint8_t *ubuf = (const uint8_t *)buf;
1626
1627  for (size_t i = 0; i < num_bps; ++i) {
1628    DNBBreakpoint *bp = bps[i];
1629
1630    const bool intersects = bp->IntersectsRange(
1631        addr, size, &intersect_addr, &intersect_size, &opcode_offset);
1632    UNUSED_IF_ASSERT_DISABLED(intersects);
1633    assert(intersects);
1634    assert(addr <= intersect_addr && intersect_addr < addr + size);
1635    assert(addr < intersect_addr + intersect_size &&
1636           intersect_addr + intersect_size <= addr + size);
1637    assert(opcode_offset + intersect_size <= bp->ByteSize());
1638
1639    // Check for bytes before this breakpoint
1640    const nub_addr_t curr_addr = addr + bytes_written;
1641    if (intersect_addr > curr_addr) {
1642      // There are some bytes before this breakpoint that we need to
1643      // just write to memory
1644      nub_size_t curr_size = intersect_addr - curr_addr;
1645      nub_size_t curr_bytes_written =
1646          m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written);
1647      bytes_written += curr_bytes_written;
1648      if (curr_bytes_written != curr_size) {
1649        // We weren't able to write all of the requested bytes, we
1650        // are done looping and will return the number of bytes that
1651        // we have written so far.
1652        break;
1653      }
1654    }
1655
1656    // Now write any bytes that would cover up any software breakpoints
1657    // directly into the breakpoint opcode buffer
1658    ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written,
1659             intersect_size);
1660    bytes_written += intersect_size;
1661  }
1662
1663  // Write any remaining bytes after the last breakpoint if we have any left
1664  if (bytes_written < size)
1665    bytes_written += m_task.WriteMemory(
1666        addr + bytes_written, size - bytes_written, ubuf + bytes_written);
1667
1668  return bytes_written;
1669}
1670
1671void MachProcess::ReplyToAllExceptions() {
1672  PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
1673  if (!m_exception_messages.empty()) {
1674    MachException::Message::iterator pos;
1675    MachException::Message::iterator begin = m_exception_messages.begin();
1676    MachException::Message::iterator end = m_exception_messages.end();
1677    for (pos = begin; pos != end; ++pos) {
1678      DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...",
1679                       (uint32_t)std::distance(begin, pos));
1680      int thread_reply_signal = 0;
1681
1682      nub_thread_t tid =
1683          m_thread_list.GetThreadIDByMachPortNumber(pos->state.thread_port);
1684      const DNBThreadResumeAction *action = NULL;
1685      if (tid != INVALID_NUB_THREAD) {
1686        action = m_thread_actions.GetActionForThread(tid, false);
1687      }
1688
1689      if (action) {
1690        thread_reply_signal = action->signal;
1691        if (thread_reply_signal)
1692          m_thread_actions.SetSignalHandledForThread(tid);
1693      }
1694
1695      DNBError err(pos->Reply(this, thread_reply_signal));
1696      if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
1697        err.LogThreadedIfError("Error replying to exception");
1698    }
1699
1700    // Erase all exception message as we should have used and replied
1701    // to them all already.
1702    m_exception_messages.clear();
1703  }
1704}
1705void MachProcess::PrivateResume() {
1706  PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
1707
1708  m_auto_resume_signo = m_sent_interrupt_signo;
1709  if (m_auto_resume_signo)
1710    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x "
1711                                  "resuming (with unhandled interrupt signal "
1712                                  "%i)...",
1713                     m_task.TaskPort(), m_auto_resume_signo);
1714  else
1715    DNBLogThreadedIf(LOG_PROCESS,
1716                     "MachProcess::PrivateResume() - task 0x%x resuming...",
1717                     m_task.TaskPort());
1718
1719  ReplyToAllExceptions();
1720  //    bool stepOverBreakInstruction = step;
1721
1722  // Let the thread prepare to resume and see if any threads want us to
1723  // step over a breakpoint instruction (ProcessWillResume will modify
1724  // the value of stepOverBreakInstruction).
1725  m_thread_list.ProcessWillResume(this, m_thread_actions);
1726
1727  // Set our state accordingly
1728  if (m_thread_actions.NumActionsWithState(eStateStepping))
1729    SetState(eStateStepping);
1730  else
1731    SetState(eStateRunning);
1732
1733  // Now resume our task.
1734  m_task.Resume();
1735}
1736
1737DNBBreakpoint *MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length,
1738                                             bool hardware) {
1739  DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = "
1740                                    "0x%8.8llx, length = %llu, hardware = %i)",
1741                   (uint64_t)addr, (uint64_t)length, hardware);
1742
1743  DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
1744  if (bp)
1745    bp->Retain();
1746  else
1747    bp = m_breakpoints.Add(addr, length, hardware);
1748
1749  if (EnableBreakpoint(addr)) {
1750    DNBLogThreadedIf(LOG_BREAKPOINTS,
1751                     "MachProcess::CreateBreakpoint ( addr = "
1752                     "0x%8.8llx, length = %llu) => %p",
1753                     (uint64_t)addr, (uint64_t)length, static_cast<void *>(bp));
1754    return bp;
1755  } else if (bp->Release() == 0) {
1756    m_breakpoints.Remove(addr);
1757  }
1758  // We failed to enable the breakpoint
1759  return NULL;
1760}
1761
1762DNBBreakpoint *MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length,
1763                                             uint32_t watch_flags,
1764                                             bool hardware) {
1765  DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = "
1766                                    "0x%8.8llx, length = %llu, flags = "
1767                                    "0x%8.8x, hardware = %i)",
1768                   (uint64_t)addr, (uint64_t)length, watch_flags, hardware);
1769
1770  DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
1771  // since the Z packets only send an address, we can only have one watchpoint
1772  // at
1773  // an address. If there is already one, we must refuse to create another
1774  // watchpoint
1775  if (wp)
1776    return NULL;
1777
1778  wp = m_watchpoints.Add(addr, length, hardware);
1779  wp->SetIsWatchpoint(watch_flags);
1780
1781  if (EnableWatchpoint(addr)) {
1782    DNBLogThreadedIf(LOG_WATCHPOINTS,
1783                     "MachProcess::CreateWatchpoint ( addr = "
1784                     "0x%8.8llx, length = %llu) => %p",
1785                     (uint64_t)addr, (uint64_t)length, static_cast<void *>(wp));
1786    return wp;
1787  } else {
1788    DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = "
1789                                      "0x%8.8llx, length = %llu) => FAILED",
1790                     (uint64_t)addr, (uint64_t)length);
1791    m_watchpoints.Remove(addr);
1792  }
1793  // We failed to enable the watchpoint
1794  return NULL;
1795}
1796
1797void MachProcess::DisableAllBreakpoints(bool remove) {
1798  DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )",
1799                   __FUNCTION__, remove);
1800
1801  m_breakpoints.DisableAllBreakpoints(this);
1802
1803  if (remove)
1804    m_breakpoints.RemoveDisabled();
1805}
1806
1807void MachProcess::DisableAllWatchpoints(bool remove) {
1808  DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )",
1809                   __FUNCTION__, remove);
1810
1811  m_watchpoints.DisableAllWatchpoints(this);
1812
1813  if (remove)
1814    m_watchpoints.RemoveDisabled();
1815}
1816
1817bool MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove) {
1818  DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
1819  if (bp) {
1820    // After "exec" we might end up with a bunch of breakpoints that were
1821    // disabled
1822    // manually, just ignore them
1823    if (!bp->IsEnabled()) {
1824      // Breakpoint might have been disabled by an exec
1825      if (remove && bp->Release() == 0) {
1826        m_thread_list.NotifyBreakpointChanged(bp);
1827        m_breakpoints.Remove(addr);
1828      }
1829      return true;
1830    }
1831
1832    // We have multiple references to this breakpoint, decrement the ref count
1833    // and if it isn't zero, then return true;
1834    if (remove && bp->Release() > 0)
1835      return true;
1836
1837    DNBLogThreadedIf(
1838        LOG_BREAKPOINTS | LOG_VERBOSE,
1839        "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )",
1840        (uint64_t)addr, remove);
1841
1842    if (bp->IsHardware()) {
1843      bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint(bp);
1844
1845      if (hw_disable_result) {
1846        bp->SetEnabled(false);
1847        // Let the thread list know that a breakpoint has been modified
1848        if (remove) {
1849          m_thread_list.NotifyBreakpointChanged(bp);
1850          m_breakpoints.Remove(addr);
1851        }
1852        DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( "
1853                                          "addr = 0x%8.8llx, remove = %d ) "
1854                                          "(hardware) => success",
1855                         (uint64_t)addr, remove);
1856        return true;
1857      }
1858
1859      return false;
1860    }
1861
1862    const nub_size_t break_op_size = bp->ByteSize();
1863    assert(break_op_size > 0);
1864    const uint8_t *const break_op =
1865        DNBArchProtocol::GetBreakpointOpcode(bp->ByteSize());
1866    if (break_op_size > 0) {
1867      // Clear a software breakpoint instruction
1868      uint8_t curr_break_op[break_op_size];
1869      bool break_op_found = false;
1870
1871      // Read the breakpoint opcode
1872      if (m_task.ReadMemory(addr, break_op_size, curr_break_op) ==
1873          break_op_size) {
1874        bool verify = false;
1875        if (bp->IsEnabled()) {
1876          // Make sure a breakpoint opcode exists at this address
1877          if (memcmp(curr_break_op, break_op, break_op_size) == 0) {
1878            break_op_found = true;
1879            // We found a valid breakpoint opcode at this address, now restore
1880            // the saved opcode.
1881            if (m_task.WriteMemory(addr, break_op_size,
1882                                   bp->SavedOpcodeBytes()) == break_op_size) {
1883              verify = true;
1884            } else {
1885              DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, "
1886                          "remove = %d ) memory write failed when restoring "
1887                          "original opcode",
1888                          (uint64_t)addr, remove);
1889            }
1890          } else {
1891            DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, "
1892                          "remove = %d ) expected a breakpoint opcode but "
1893                          "didn't find one.",
1894                          (uint64_t)addr, remove);
1895            // Set verify to true and so we can check if the original opcode has
1896            // already been restored
1897            verify = true;
1898          }
1899        } else {
1900          DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE,
1901                           "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, "
1902                           "remove = %d ) is not enabled",
1903                           (uint64_t)addr, remove);
1904          // Set verify to true and so we can check if the original opcode is
1905          // there
1906          verify = true;
1907        }
1908
1909        if (verify) {
1910          uint8_t verify_opcode[break_op_size];
1911          // Verify that our original opcode made it back to the inferior
1912          if (m_task.ReadMemory(addr, break_op_size, verify_opcode) ==
1913              break_op_size) {
1914            // compare the memory we just read with the original opcode
1915            if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) ==
1916                0) {
1917              // SUCCESS
1918              bp->SetEnabled(false);
1919              // Let the thread list know that a breakpoint has been modified
1920              if (remove && bp->Release() == 0) {
1921                m_thread_list.NotifyBreakpointChanged(bp);
1922                m_breakpoints.Remove(addr);
1923              }
1924              DNBLogThreadedIf(LOG_BREAKPOINTS,
1925                               "MachProcess::DisableBreakpoint ( addr = "
1926                               "0x%8.8llx, remove = %d ) => success",
1927                               (uint64_t)addr, remove);
1928              return true;
1929            } else {
1930              if (break_op_found)
1931                DNBLogError("MachProcess::DisableBreakpoint ( addr = "
1932                            "0x%8.8llx, remove = %d ) : failed to restore "
1933                            "original opcode",
1934                            (uint64_t)addr, remove);
1935              else
1936                DNBLogError("MachProcess::DisableBreakpoint ( addr = "
1937                            "0x%8.8llx, remove = %d ) : opcode changed",
1938                            (uint64_t)addr, remove);
1939            }
1940          } else {
1941            DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable "
1942                          "breakpoint 0x%8.8llx",
1943                          (uint64_t)addr);
1944          }
1945        }
1946      } else {
1947        DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory "
1948                      "at 0x%8.8llx",
1949                      (uint64_t)addr);
1950      }
1951    }
1952  } else {
1953    DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = "
1954                "%d ) invalid breakpoint address",
1955                (uint64_t)addr, remove);
1956  }
1957  return false;
1958}
1959
1960bool MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove) {
1961  DNBLogThreadedIf(LOG_WATCHPOINTS,
1962                   "MachProcess::%s(addr = 0x%8.8llx, remove = %d)",
1963                   __FUNCTION__, (uint64_t)addr, remove);
1964  DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
1965  if (wp) {
1966    // If we have multiple references to a watchpoint, removing the watchpoint
1967    // shouldn't clear it
1968    if (remove && wp->Release() > 0)
1969      return true;
1970
1971    nub_addr_t addr = wp->Address();
1972    DNBLogThreadedIf(
1973        LOG_WATCHPOINTS,
1974        "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )",
1975        (uint64_t)addr, remove);
1976
1977    if (wp->IsHardware()) {
1978      bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint(wp);
1979
1980      if (hw_disable_result) {
1981        wp->SetEnabled(false);
1982        if (remove)
1983          m_watchpoints.Remove(addr);
1984        DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( "
1985                                          "addr = 0x%8.8llx, remove = %d ) "
1986                                          "(hardware) => success",
1987                         (uint64_t)addr, remove);
1988        return true;
1989      }
1990    }
1991
1992    // TODO: clear software watchpoints if we implement them
1993  } else {
1994    DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = "
1995                "%d ) invalid watchpoint ID",
1996                (uint64_t)addr, remove);
1997  }
1998  return false;
1999}
2000
2001uint32_t MachProcess::GetNumSupportedHardwareWatchpoints() const {
2002  return m_thread_list.NumSupportedHardwareWatchpoints();
2003}
2004
2005bool MachProcess::EnableBreakpoint(nub_addr_t addr) {
2006  DNBLogThreadedIf(LOG_BREAKPOINTS,
2007                   "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )",
2008                   (uint64_t)addr);
2009  DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
2010  if (bp) {
2011    if (bp->IsEnabled()) {
2012      DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
2013                    "breakpoint already enabled.",
2014                    (uint64_t)addr);
2015      return true;
2016    } else {
2017      if (bp->HardwarePreferred()) {
2018        bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp));
2019        if (bp->IsHardware()) {
2020          bp->SetEnabled(true);
2021          return true;
2022        }
2023      }
2024
2025      const nub_size_t break_op_size = bp->ByteSize();
2026      assert(break_op_size != 0);
2027      const uint8_t *const break_op =
2028          DNBArchProtocol::GetBreakpointOpcode(break_op_size);
2029      if (break_op_size > 0) {
2030        // Save the original opcode by reading it
2031        if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) ==
2032            break_op_size) {
2033          // Write a software breakpoint in place of the original opcode
2034          if (m_task.WriteMemory(addr, break_op_size, break_op) ==
2035              break_op_size) {
2036            uint8_t verify_break_op[4];
2037            if (m_task.ReadMemory(addr, break_op_size, verify_break_op) ==
2038                break_op_size) {
2039              if (memcmp(break_op, verify_break_op, break_op_size) == 0) {
2040                bp->SetEnabled(true);
2041                // Let the thread list know that a breakpoint has been modified
2042                m_thread_list.NotifyBreakpointChanged(bp);
2043                DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::"
2044                                                  "EnableBreakpoint ( addr = "
2045                                                  "0x%8.8llx ) : SUCCESS.",
2046                                 (uint64_t)addr);
2047                return true;
2048              } else {
2049                DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx "
2050                            "): breakpoint opcode verification failed.",
2051                            (uint64_t)addr);
2052              }
2053            } else {
2054              DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
2055                          "unable to read memory to verify breakpoint opcode.",
2056                          (uint64_t)addr);
2057            }
2058          } else {
2059            DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
2060                        "unable to write breakpoint opcode to memory.",
2061                        (uint64_t)addr);
2062          }
2063        } else {
2064          DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
2065                      "unable to read memory at breakpoint address.",
2066                      (uint64_t)addr);
2067        }
2068      } else {
2069        DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no "
2070                    "software breakpoint opcode for current architecture.",
2071                    (uint64_t)addr);
2072      }
2073    }
2074  }
2075  return false;
2076}
2077
2078bool MachProcess::EnableWatchpoint(nub_addr_t addr) {
2079  DNBLogThreadedIf(LOG_WATCHPOINTS,
2080                   "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)",
2081                   (uint64_t)addr);
2082  DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
2083  if (wp) {
2084    nub_addr_t addr = wp->Address();
2085    if (wp->IsEnabled()) {
2086      DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): "
2087                    "watchpoint already enabled.",
2088                    (uint64_t)addr);
2089      return true;
2090    } else {
2091      // Currently only try and set hardware watchpoints.
2092      wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp));
2093      if (wp->IsHardware()) {
2094        wp->SetEnabled(true);
2095        return true;
2096      }
2097      // TODO: Add software watchpoints by doing page protection tricks.
2098    }
2099  }
2100  return false;
2101}
2102
2103// Called by the exception thread when an exception has been received from
2104// our process. The exception message is completely filled and the exception
2105// data has already been copied.
2106void MachProcess::ExceptionMessageReceived(
2107    const MachException::Message &exceptionMessage) {
2108  PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
2109
2110  if (m_exception_messages.empty())
2111    m_task.Suspend();
2112
2113  DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
2114
2115  // Use a locker to automatically unlock our mutex in case of exceptions
2116  // Add the exception to our internal exception stack
2117  m_exception_messages.push_back(exceptionMessage);
2118}
2119
2120task_t MachProcess::ExceptionMessageBundleComplete() {
2121  // We have a complete bundle of exceptions for our child process.
2122  PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
2123  DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.",
2124                   __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
2125  bool auto_resume = false;
2126  if (!m_exception_messages.empty()) {
2127    m_did_exec = false;
2128    // First check for any SIGTRAP and make sure we didn't exec
2129    const task_t task = m_task.TaskPort();
2130    size_t i;
2131    if (m_pid != 0) {
2132      bool received_interrupt = false;
2133      uint32_t num_task_exceptions = 0;
2134      for (i = 0; i < m_exception_messages.size(); ++i) {
2135        if (m_exception_messages[i].state.task_port == task) {
2136          ++num_task_exceptions;
2137          const int signo = m_exception_messages[i].state.SoftSignal();
2138          if (signo == SIGTRAP) {
2139            // SIGTRAP could mean that we exec'ed. We need to check the
2140            // dyld all_image_infos.infoArray to see if it is NULL and if
2141            // so, say that we exec'ed.
2142            const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress();
2143            if (aii_addr != INVALID_NUB_ADDRESS) {
2144              const nub_addr_t info_array_count_addr = aii_addr + 4;
2145              uint32_t info_array_count = 0;
2146              if (m_task.ReadMemory(info_array_count_addr, 4,
2147                                    &info_array_count) == 4) {
2148                if (info_array_count == 0) {
2149                  m_did_exec = true;
2150                  // Force the task port to update itself in case the task port
2151                  // changed after exec
2152                  DNBError err;
2153                  const task_t old_task = m_task.TaskPort();
2154                  const task_t new_task =
2155                      m_task.TaskPortForProcessID(err, true);
2156                  if (old_task != new_task)
2157                    DNBLogThreadedIf(
2158                        LOG_PROCESS,
2159                        "exec: task changed from 0x%4.4x to 0x%4.4x", old_task,
2160                        new_task);
2161                }
2162              } else {
2163                DNBLog("error: failed to read all_image_infos.infoArrayCount "
2164                       "from 0x%8.8llx",
2165                       (uint64_t)info_array_count_addr);
2166              }
2167            }
2168            break;
2169          } else if (m_sent_interrupt_signo != 0 &&
2170                     signo == m_sent_interrupt_signo) {
2171            received_interrupt = true;
2172          }
2173        }
2174      }
2175
2176      if (m_did_exec) {
2177        cpu_type_t process_cpu_type =
2178            MachProcess::GetCPUTypeForLocalProcess(m_pid);
2179        if (m_cpu_type != process_cpu_type) {
2180          DNBLog("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type,
2181                 process_cpu_type);
2182          m_cpu_type = process_cpu_type;
2183          DNBArchProtocol::SetArchitecture(process_cpu_type);
2184        }
2185        m_thread_list.Clear();
2186        m_activities.Clear();
2187        m_breakpoints.DisableAll();
2188      }
2189
2190      if (m_sent_interrupt_signo != 0) {
2191        if (received_interrupt) {
2192          DNBLogThreadedIf(LOG_PROCESS,
2193                           "MachProcess::ExceptionMessageBundleComplete(): "
2194                           "process successfully interrupted with signal %i",
2195                           m_sent_interrupt_signo);
2196
2197          // Mark that we received the interrupt signal
2198          m_sent_interrupt_signo = 0;
2199          // Not check if we had a case where:
2200          // 1 - We called MachProcess::Interrupt() but we stopped for another
2201          // reason
2202          // 2 - We called MachProcess::Resume() (but still haven't gotten the
2203          // interrupt signal)
2204          // 3 - We are now incorrectly stopped because we are handling the
2205          // interrupt signal we missed
2206          // 4 - We might need to resume if we stopped only with the interrupt
2207          // signal that we never handled
2208          if (m_auto_resume_signo != 0) {
2209            // Only auto_resume if we stopped with _only_ the interrupt signal
2210            if (num_task_exceptions == 1) {
2211              auto_resume = true;
2212              DNBLogThreadedIf(LOG_PROCESS, "MachProcess::"
2213                                            "ExceptionMessageBundleComplete(): "
2214                                            "auto resuming due to unhandled "
2215                                            "interrupt signal %i",
2216                               m_auto_resume_signo);
2217            }
2218            m_auto_resume_signo = 0;
2219          }
2220        } else {
2221          DNBLogThreadedIf(LOG_PROCESS, "MachProcess::"
2222                                        "ExceptionMessageBundleComplete(): "
2223                                        "didn't get signal %i after "
2224                                        "MachProcess::Interrupt()",
2225                           m_sent_interrupt_signo);
2226        }
2227      }
2228    }
2229
2230    // Let all threads recover from stopping and do any clean up based
2231    // on the previous thread state (if any).
2232    m_thread_list.ProcessDidStop(this);
2233    m_activities.Clear();
2234
2235    // Let each thread know of any exceptions
2236    for (i = 0; i < m_exception_messages.size(); ++i) {
2237      // Let the thread list figure use the MachProcess to forward all
2238      // exceptions
2239      // on down to each thread.
2240      if (m_exception_messages[i].state.task_port == task)
2241        m_thread_list.NotifyException(m_exception_messages[i].state);
2242      if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
2243        m_exception_messages[i].Dump();
2244    }
2245
2246    if (DNBLogCheckLogBit(LOG_THREAD))
2247      m_thread_list.Dump();
2248
2249    bool step_more = false;
2250    if (m_thread_list.ShouldStop(step_more) && !auto_resume) {
2251      // Wait for the eEventProcessRunningStateChanged event to be reset
2252      // before changing state to stopped to avoid race condition with
2253      // very fast start/stops
2254      struct timespec timeout;
2255      // DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000);   // Wait for 250
2256      // ms
2257      DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms
2258      m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout);
2259      SetState(eStateStopped);
2260    } else {
2261      // Resume without checking our current state.
2262      PrivateResume();
2263    }
2264  } else {
2265    DNBLogThreadedIf(
2266        LOG_EXCEPTIONS, "%s empty exception messages bundle (%llu exceptions).",
2267        __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
2268  }
2269  return m_task.TaskPort();
2270}
2271
2272nub_size_t
2273MachProcess::CopyImageInfos(struct DNBExecutableImageInfo **image_infos,
2274                            bool only_changed) {
2275  if (m_image_infos_callback != NULL)
2276    return m_image_infos_callback(ProcessID(), image_infos, only_changed,
2277                                  m_image_infos_baton);
2278  return 0;
2279}
2280
2281void MachProcess::SharedLibrariesUpdated() {
2282  uint32_t event_bits = eEventSharedLibsStateChange;
2283  // Set the shared library event bit to let clients know of shared library
2284  // changes
2285  m_events.SetEvents(event_bits);
2286  // Wait for the event bit to reset if a reset ACK is requested
2287  m_events.WaitForResetAck(event_bits);
2288}
2289
2290void MachProcess::SetExitInfo(const char *info) {
2291  if (info && info[0]) {
2292    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(\"%s\")", __FUNCTION__,
2293                     info);
2294    m_exit_info.assign(info);
2295  } else {
2296    DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(NULL)", __FUNCTION__);
2297    m_exit_info.clear();
2298  }
2299}
2300
2301void MachProcess::AppendSTDOUT(char *s, size_t len) {
2302  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%llu> %s) ...", __FUNCTION__,
2303                   (uint64_t)len, s);
2304  PTHREAD_MUTEX_LOCKER(locker, m_stdio_mutex);
2305  m_stdout_data.append(s, len);
2306  m_events.SetEvents(eEventStdioAvailable);
2307
2308  // Wait for the event bit to reset if a reset ACK is requested
2309  m_events.WaitForResetAck(eEventStdioAvailable);
2310}
2311
2312size_t MachProcess::GetAvailableSTDOUT(char *buf, size_t buf_size) {
2313  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__,
2314                   static_cast<void *>(buf), (uint64_t)buf_size);
2315  PTHREAD_MUTEX_LOCKER(locker, m_stdio_mutex);
2316  size_t bytes_available = m_stdout_data.size();
2317  if (bytes_available > 0) {
2318    if (bytes_available > buf_size) {
2319      memcpy(buf, m_stdout_data.data(), buf_size);
2320      m_stdout_data.erase(0, buf_size);
2321      bytes_available = buf_size;
2322    } else {
2323      memcpy(buf, m_stdout_data.data(), bytes_available);
2324      m_stdout_data.clear();
2325    }
2326  }
2327  return bytes_available;
2328}
2329
2330nub_addr_t MachProcess::GetDYLDAllImageInfosAddress() {
2331  DNBError err;
2332  return m_task.GetDYLDAllImageInfosAddress(err);
2333}
2334
2335size_t MachProcess::GetAvailableSTDERR(char *buf, size_t buf_size) { return 0; }
2336
2337void *MachProcess::STDIOThread(void *arg) {
2338  MachProcess *proc = (MachProcess *)arg;
2339  DNBLogThreadedIf(LOG_PROCESS,
2340                   "MachProcess::%s ( arg = %p ) thread starting...",
2341                   __FUNCTION__, arg);
2342
2343#if defined(__APPLE__)
2344  pthread_setname_np("stdio monitoring thread");
2345#endif
2346
2347  // We start use a base and more options so we can control if we
2348  // are currently using a timeout on the mach_msg. We do this to get a
2349  // bunch of related exceptions on our exception port so we can process
2350  // then together. When we have multiple threads, we can get an exception
2351  // per thread and they will come in consecutively. The main thread loop
2352  // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
2353  // flag set in the options, so we will wait forever for an exception on
2354  // our exception port. After we get one exception, we then will use the
2355  // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
2356  // exceptions for our process. After we have received the last pending
2357  // exception, we will get a timeout which enables us to then notify
2358  // our main thread that we have an exception bundle available. We then wait
2359  // for the main thread to tell this exception thread to start trying to get
2360  // exceptions messages again and we start again with a mach_msg read with
2361  // infinite timeout.
2362  DNBError err;
2363  int stdout_fd = proc->GetStdoutFileDescriptor();
2364  int stderr_fd = proc->GetStderrFileDescriptor();
2365  if (stdout_fd == stderr_fd)
2366    stderr_fd = -1;
2367
2368  while (stdout_fd >= 0 || stderr_fd >= 0) {
2369    ::pthread_testcancel();
2370
2371    fd_set read_fds;
2372    FD_ZERO(&read_fds);
2373    if (stdout_fd >= 0)
2374      FD_SET(stdout_fd, &read_fds);
2375    if (stderr_fd >= 0)
2376      FD_SET(stderr_fd, &read_fds);
2377    int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
2378
2379    int num_set_fds = select(nfds, &read_fds, NULL, NULL, NULL);
2380    DNBLogThreadedIf(LOG_PROCESS,
2381                     "select (nfds, &read_fds, NULL, NULL, NULL) => %d",
2382                     num_set_fds);
2383
2384    if (num_set_fds < 0) {
2385      int select_errno = errno;
2386      if (DNBLogCheckLogBit(LOG_PROCESS)) {
2387        err.SetError(select_errno, DNBError::POSIX);
2388        err.LogThreadedIfError(
2389            "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
2390      }
2391
2392      switch (select_errno) {
2393      case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate
2394                   // the requested number of file descriptors, or we have
2395                   // non-blocking IO
2396        break;
2397      case EBADF: // One of the descriptor sets specified an invalid descriptor.
2398        return NULL;
2399        break;
2400      case EINTR:  // A signal was delivered before the time limit expired and
2401                   // before any of the selected events occurred.
2402      case EINVAL: // The specified time limit is invalid. One of its components
2403                   // is negative or too large.
2404      default:     // Other unknown error
2405        break;
2406      }
2407    } else if (num_set_fds == 0) {
2408    } else {
2409      char s[1024];
2410      s[sizeof(s) - 1] = '\0'; // Ensure we have NULL termination
2411      ssize_t bytes_read = 0;
2412      if (stdout_fd >= 0 && FD_ISSET(stdout_fd, &read_fds)) {
2413        do {
2414          bytes_read = ::read(stdout_fd, s, sizeof(s) - 1);
2415          if (bytes_read < 0) {
2416            int read_errno = errno;
2417            DNBLogThreadedIf(LOG_PROCESS,
2418                             "read (stdout_fd, ) => %zd   errno: %d (%s)",
2419                             bytes_read, read_errno, strerror(read_errno));
2420          } else if (bytes_read == 0) {
2421            // EOF...
2422            DNBLogThreadedIf(
2423                LOG_PROCESS,
2424                "read (stdout_fd, ) => %zd  (reached EOF for child STDOUT)",
2425                bytes_read);
2426            stdout_fd = -1;
2427          } else if (bytes_read > 0) {
2428            proc->AppendSTDOUT(s, bytes_read);
2429          }
2430
2431        } while (bytes_read > 0);
2432      }
2433
2434      if (stderr_fd >= 0 && FD_ISSET(stderr_fd, &read_fds)) {
2435        do {
2436          bytes_read = ::read(stderr_fd, s, sizeof(s) - 1);
2437          if (bytes_read < 0) {
2438            int read_errno = errno;
2439            DNBLogThreadedIf(LOG_PROCESS,
2440                             "read (stderr_fd, ) => %zd   errno: %d (%s)",
2441                             bytes_read, read_errno, strerror(read_errno));
2442          } else if (bytes_read == 0) {
2443            // EOF...
2444            DNBLogThreadedIf(
2445                LOG_PROCESS,
2446                "read (stderr_fd, ) => %zd  (reached EOF for child STDERR)",
2447                bytes_read);
2448            stderr_fd = -1;
2449          } else if (bytes_read > 0) {
2450            proc->AppendSTDOUT(s, bytes_read);
2451          }
2452
2453        } while (bytes_read > 0);
2454      }
2455    }
2456  }
2457  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...",
2458                   __FUNCTION__, arg);
2459  return NULL;
2460}
2461
2462void MachProcess::SignalAsyncProfileData(const char *info) {
2463  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info);
2464  PTHREAD_MUTEX_LOCKER(locker, m_profile_data_mutex);
2465  m_profile_data.push_back(info);
2466  m_events.SetEvents(eEventProfileDataAvailable);
2467
2468  // Wait for the event bit to reset if a reset ACK is requested
2469  m_events.WaitForResetAck(eEventProfileDataAvailable);
2470}
2471
2472size_t MachProcess::GetAsyncProfileData(char *buf, size_t buf_size) {
2473  DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__,
2474                   static_cast<void *>(buf), (uint64_t)buf_size);
2475  PTHREAD_MUTEX_LOCKER(locker, m_profile_data_mutex);
2476  if (m_profile_data.empty())
2477    return 0;
2478
2479  size_t bytes_available = m_profile_data.front().size();
2480  if (bytes_available > 0) {
2481    if (bytes_available > buf_size) {
2482      memcpy(buf, m_profile_data.front().data(), buf_size);
2483      m_profile_data.front().erase(0, buf_size);
2484      bytes_available = buf_size;
2485    } else {
2486      memcpy(buf, m_profile_data.front().data(), bytes_available);
2487      m_profile_data.erase(m_profile_data.begin());
2488    }
2489  }
2490  return bytes_available;
2491}
2492
2493void *MachProcess::ProfileThread(void *arg) {
2494  MachProcess *proc = (MachProcess *)arg;
2495  DNBLogThreadedIf(LOG_PROCESS,
2496                   "MachProcess::%s ( arg = %p ) thread starting...",
2497                   __FUNCTION__, arg);
2498
2499#if defined(__APPLE__)
2500  pthread_setname_np("performance profiling thread");
2501#endif
2502
2503  while (proc->IsProfilingEnabled()) {
2504    nub_state_t state = proc->GetState();
2505    if (state == eStateRunning) {
2506      std::string data =
2507          proc->Task().GetProfileData(proc->GetProfileScanType());
2508      if (!data.empty()) {
2509        proc->SignalAsyncProfileData(data.c_str());
2510      }
2511    } else if ((state == eStateUnloaded) || (state == eStateDetached) ||
2512               (state == eStateUnloaded)) {
2513      // Done. Get out of this thread.
2514      break;
2515    }
2516
2517    // A simple way to set up the profile interval. We can also use select() or
2518    // dispatch timer source if necessary.
2519    usleep(proc->ProfileInterval());
2520  }
2521  return NULL;
2522}
2523
2524pid_t MachProcess::AttachForDebug(pid_t pid, char *err_str, size_t err_len) {
2525  // Clear out and clean up from any current state
2526  Clear();
2527  if (pid != 0) {
2528    DNBError err;
2529    // Make sure the process exists...
2530    if (::getpgid(pid) < 0) {
2531      err.SetErrorToErrno();
2532      const char *err_cstr = err.AsString();
2533      ::snprintf(err_str, err_len, "%s",
2534                 err_cstr ? err_cstr : "No such process");
2535      DNBLogError ("MachProcess::AttachForDebug pid %d does not exist", pid);
2536      return INVALID_NUB_PROCESS;
2537    }
2538
2539    SetState(eStateAttaching);
2540    m_pid = pid;
2541    if (!m_task.StartExceptionThread(err)) {
2542      const char *err_cstr = err.AsString();
2543      ::snprintf(err_str, err_len, "%s",
2544                 err_cstr ? err_cstr : "unable to start the exception thread");
2545      DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
2546      DNBLogError ("MachProcess::AttachForDebug failed to start exception thread: %s", err_str);
2547      m_pid = INVALID_NUB_PROCESS;
2548      return INVALID_NUB_PROCESS;
2549    }
2550
2551    errno = 0;
2552    if (::ptrace(PT_ATTACHEXC, pid, 0, 0)) {
2553      err.SetError(errno);
2554      DNBLogError ("MachProcess::AttachForDebug failed to ptrace(PT_ATTACHEXC): %s", err.AsString());
2555    } else {
2556      err.Clear();
2557    }
2558
2559    if (err.Success()) {
2560      m_flags |= eMachProcessFlagsAttached;
2561      // Sleep a bit to let the exception get received and set our process
2562      // status
2563      // to stopped.
2564      ::usleep(250000);
2565      DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
2566      return m_pid;
2567    } else {
2568      ::snprintf(err_str, err_len, "%s", err.AsString());
2569      DNBLogError ("MachProcess::AttachForDebug error: failed to attach to pid %d", pid);
2570
2571      struct kinfo_proc kinfo;
2572      int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
2573      size_t len = sizeof(struct kinfo_proc);
2574      if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kinfo, &len, NULL, 0) == 0 && len > 0) {
2575        if (kinfo.kp_proc.p_flag & P_TRACED) {
2576          ::snprintf(err_str, err_len, "%s - process %d is already being debugged", err.AsString(), pid);
2577          DNBLogError ("MachProcess::AttachForDebug pid %d is already being debugged", pid);
2578        }
2579      }
2580    }
2581  }
2582  return INVALID_NUB_PROCESS;
2583}
2584
2585Genealogy::ThreadActivitySP
2586MachProcess::GetGenealogyInfoForThread(nub_thread_t tid, bool &timed_out) {
2587  return m_activities.GetGenealogyInfoForThread(m_pid, tid, m_thread_list,
2588                                                m_task.TaskPort(), timed_out);
2589}
2590
2591Genealogy::ProcessExecutableInfoSP
2592MachProcess::GetGenealogyImageInfo(size_t idx) {
2593  return m_activities.GetProcessExecutableInfosAtIndex(idx);
2594}
2595
2596bool MachProcess::GetOSVersionNumbers(uint64_t *major, uint64_t *minor,
2597                                      uint64_t *patch) {
2598#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&                  \
2599    (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101000)
2600  return false;
2601#else
2602  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2603
2604  NSOperatingSystemVersion vers =
2605      [[NSProcessInfo processInfo] operatingSystemVersion];
2606  if (major)
2607    *major = vers.majorVersion;
2608  if (minor)
2609    *minor = vers.minorVersion;
2610  if (patch)
2611    *patch = vers.patchVersion;
2612
2613  [pool drain];
2614
2615  return true;
2616#endif
2617}
2618
2619std::string MachProcess::GetMacCatalystVersionString() {
2620  @autoreleasepool {
2621    NSDictionary *version_info =
2622      [NSDictionary dictionaryWithContentsOfFile:
2623       @"/System/Library/CoreServices/SystemVersion.plist"];
2624    NSString *version_value = [version_info objectForKey: @"iOSSupportVersion"];
2625    if (const char *version_str = [version_value UTF8String])
2626      return version_str;
2627  }
2628  return {};
2629}
2630
2631// Do the process specific setup for attach.  If this returns NULL, then there's
2632// no
2633// platform specific stuff to be done to wait for the attach.  If you get
2634// non-null,
2635// pass that token to the CheckForProcess method, and then to
2636// CleanupAfterAttach.
2637
2638//  Call PrepareForAttach before attaching to a process that has not yet
2639//  launched
2640// This returns a token that can be passed to CheckForProcess, and to
2641// CleanupAfterAttach.
2642// You should call CleanupAfterAttach to free the token, and do whatever other
2643// cleanup seems good.
2644
2645const void *MachProcess::PrepareForAttach(const char *path,
2646                                          nub_launch_flavor_t launch_flavor,
2647                                          bool waitfor, DNBError &attach_err) {
2648#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS)
2649  // Tell SpringBoard to halt the next launch of this application on startup.
2650
2651  if (!waitfor)
2652    return NULL;
2653
2654  const char *app_ext = strstr(path, ".app");
2655  const bool is_app =
2656      app_ext != NULL && (app_ext[4] == '\0' || app_ext[4] == '/');
2657  if (!is_app) {
2658    DNBLogThreadedIf(
2659        LOG_PROCESS,
2660        "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, "
2661        "we can't tell springboard to wait for launch...",
2662        path);
2663    return NULL;
2664  }
2665
2666#if defined(WITH_FBS)
2667  if (launch_flavor == eLaunchFlavorDefault)
2668    launch_flavor = eLaunchFlavorFBS;
2669  if (launch_flavor != eLaunchFlavorFBS)
2670    return NULL;
2671#elif defined(WITH_BKS)
2672  if (launch_flavor == eLaunchFlavorDefault)
2673    launch_flavor = eLaunchFlavorBKS;
2674  if (launch_flavor != eLaunchFlavorBKS)
2675    return NULL;
2676#elif defined(WITH_SPRINGBOARD)
2677  if (launch_flavor == eLaunchFlavorDefault)
2678    launch_flavor = eLaunchFlavorSpringBoard;
2679  if (launch_flavor != eLaunchFlavorSpringBoard)
2680    return NULL;
2681#endif
2682
2683  std::string app_bundle_path(path, app_ext + strlen(".app"));
2684
2685  CFStringRef bundleIDCFStr =
2686      CopyBundleIDForPath(app_bundle_path.c_str(), attach_err);
2687  std::string bundleIDStr;
2688  CFString::UTF8(bundleIDCFStr, bundleIDStr);
2689  DNBLogThreadedIf(LOG_PROCESS,
2690                   "CopyBundleIDForPath (%s, err_str) returned @\"%s\"",
2691                   app_bundle_path.c_str(), bundleIDStr.c_str());
2692
2693  if (bundleIDCFStr == NULL) {
2694    return NULL;
2695  }
2696
2697#if defined(WITH_FBS)
2698  if (launch_flavor == eLaunchFlavorFBS) {
2699    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2700
2701    NSString *stdio_path = nil;
2702    NSFileManager *file_manager = [NSFileManager defaultManager];
2703    const char *null_path = "/dev/null";
2704    stdio_path =
2705        [file_manager stringWithFileSystemRepresentation:null_path
2706                                                  length:strlen(null_path)];
2707
2708    NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
2709    NSMutableDictionary *options = [NSMutableDictionary dictionary];
2710
2711    DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: "
2712                                  "@\"%s\",options include stdio path: \"%s\", "
2713                                  "BKSDebugOptionKeyDebugOnNextLaunch & "
2714                                  "BKSDebugOptionKeyWaitForDebugger )",
2715                     bundleIDStr.c_str(), null_path);
2716
2717    [debug_options setObject:stdio_path
2718                      forKey:FBSDebugOptionKeyStandardOutPath];
2719    [debug_options setObject:stdio_path
2720                      forKey:FBSDebugOptionKeyStandardErrorPath];
2721    [debug_options setObject:[NSNumber numberWithBool:YES]
2722                      forKey:FBSDebugOptionKeyWaitForDebugger];
2723    [debug_options setObject:[NSNumber numberWithBool:YES]
2724                      forKey:FBSDebugOptionKeyDebugOnNextLaunch];
2725
2726    [options setObject:debug_options
2727                forKey:FBSOpenApplicationOptionKeyDebuggingOptions];
2728
2729    FBSSystemService *system_service = [[FBSSystemService alloc] init];
2730
2731    mach_port_t client_port = [system_service createClientPort];
2732    __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
2733    __block FBSOpenApplicationErrorCode attach_error_code =
2734        FBSOpenApplicationErrorCodeNone;
2735
2736    NSString *bundleIDNSStr = (NSString *)bundleIDCFStr;
2737
2738    [system_service openApplication:bundleIDNSStr
2739                            options:options
2740                         clientPort:client_port
2741                         withResult:^(NSError *error) {
2742                           // The system service will cleanup the client port we
2743                           // created for us.
2744                           if (error)
2745                             attach_error_code =
2746                                 (FBSOpenApplicationErrorCode)[error code];
2747
2748                           [system_service release];
2749                           dispatch_semaphore_signal(semaphore);
2750                         }];
2751
2752    const uint32_t timeout_secs = 9;
2753
2754    dispatch_time_t timeout =
2755        dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
2756
2757    long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
2758
2759    if (!success) {
2760      DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
2761      attach_err.SetErrorString(
2762          "debugserver timed out waiting for openApplication to complete.");
2763      attach_err.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
2764    } else if (attach_error_code != FBSOpenApplicationErrorCodeNone) {
2765      std::string empty_str;
2766      SetFBSError(attach_error_code, empty_str, attach_err);
2767      DNBLogError("unable to launch the application with CFBundleIdentifier "
2768                  "'%s' bks_error = %ld",
2769                  bundleIDStr.c_str(), (NSInteger)attach_error_code);
2770    }
2771    dispatch_release(semaphore);
2772    [pool drain];
2773  }
2774#endif
2775#if defined(WITH_BKS)
2776  if (launch_flavor == eLaunchFlavorBKS) {
2777    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2778
2779    NSString *stdio_path = nil;
2780    NSFileManager *file_manager = [NSFileManager defaultManager];
2781    const char *null_path = "/dev/null";
2782    stdio_path =
2783        [file_manager stringWithFileSystemRepresentation:null_path
2784                                                  length:strlen(null_path)];
2785
2786    NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
2787    NSMutableDictionary *options = [NSMutableDictionary dictionary];
2788
2789    DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: "
2790                                  "@\"%s\",options include stdio path: \"%s\", "
2791                                  "BKSDebugOptionKeyDebugOnNextLaunch & "
2792                                  "BKSDebugOptionKeyWaitForDebugger )",
2793                     bundleIDStr.c_str(), null_path);
2794
2795    [debug_options setObject:stdio_path
2796                      forKey:BKSDebugOptionKeyStandardOutPath];
2797    [debug_options setObject:stdio_path
2798                      forKey:BKSDebugOptionKeyStandardErrorPath];
2799    [debug_options setObject:[NSNumber numberWithBool:YES]
2800                      forKey:BKSDebugOptionKeyWaitForDebugger];
2801    [debug_options setObject:[NSNumber numberWithBool:YES]
2802                      forKey:BKSDebugOptionKeyDebugOnNextLaunch];
2803
2804    [options setObject:debug_options
2805                forKey:BKSOpenApplicationOptionKeyDebuggingOptions];
2806
2807    BKSSystemService *system_service = [[BKSSystemService alloc] init];
2808
2809    mach_port_t client_port = [system_service createClientPort];
2810    __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
2811    __block BKSOpenApplicationErrorCode attach_error_code =
2812        BKSOpenApplicationErrorCodeNone;
2813
2814    NSString *bundleIDNSStr = (NSString *)bundleIDCFStr;
2815
2816    [system_service openApplication:bundleIDNSStr
2817                            options:options
2818                         clientPort:client_port
2819                         withResult:^(NSError *error) {
2820                           // The system service will cleanup the client port we
2821                           // created for us.
2822                           if (error)
2823                             attach_error_code =
2824                                 (BKSOpenApplicationErrorCode)[error code];
2825
2826                           [system_service release];
2827                           dispatch_semaphore_signal(semaphore);
2828                         }];
2829
2830    const uint32_t timeout_secs = 9;
2831
2832    dispatch_time_t timeout =
2833        dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
2834
2835    long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
2836
2837    if (!success) {
2838      DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
2839      attach_err.SetErrorString(
2840          "debugserver timed out waiting for openApplication to complete.");
2841      attach_err.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
2842    } else if (attach_error_code != BKSOpenApplicationErrorCodeNone) {
2843      std::string empty_str;
2844      SetBKSError(attach_error_code, empty_str, attach_err);
2845      DNBLogError("unable to launch the application with CFBundleIdentifier "
2846                  "'%s' bks_error = %ld",
2847                  bundleIDStr.c_str(), attach_error_code);
2848    }
2849    dispatch_release(semaphore);
2850    [pool drain];
2851  }
2852#endif
2853
2854#if defined(WITH_SPRINGBOARD)
2855  if (launch_flavor == eLaunchFlavorSpringBoard) {
2856    SBSApplicationLaunchError sbs_error = 0;
2857
2858    const char *stdout_err = "/dev/null";
2859    CFString stdio_path;
2860    stdio_path.SetFileSystemRepresentation(stdout_err);
2861
2862    DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" "
2863                                  ", NULL, NULL, NULL, @\"%s\", @\"%s\", "
2864                                  "SBSApplicationDebugOnNextLaunch | "
2865                                  "SBSApplicationLaunchWaitForDebugger )",
2866                     bundleIDStr.c_str(), stdout_err, stdout_err);
2867
2868    sbs_error = SBSLaunchApplicationForDebugging(
2869        bundleIDCFStr,
2870        (CFURLRef)NULL, // openURL
2871        NULL,           // launch_argv.get(),
2872        NULL,           // launch_envp.get(),  // CFDictionaryRef environment
2873        stdio_path.get(), stdio_path.get(),
2874        SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger);
2875
2876    if (sbs_error != SBSApplicationLaunchErrorSuccess) {
2877      attach_err.SetError(sbs_error, DNBError::SpringBoard);
2878      return NULL;
2879    }
2880  }
2881#endif // WITH_SPRINGBOARD
2882
2883  DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
2884  return bundleIDCFStr;
2885#else // !(defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined
2886      // (WITH_FBS))
2887  return NULL;
2888#endif
2889}
2890
2891// Pass in the token you got from PrepareForAttach.  If there is a process
2892// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS
2893// will be returned.
2894
2895nub_process_t MachProcess::CheckForProcess(const void *attach_token,
2896                                           nub_launch_flavor_t launch_flavor) {
2897  if (attach_token == NULL)
2898    return INVALID_NUB_PROCESS;
2899
2900#if defined(WITH_FBS)
2901  if (launch_flavor == eLaunchFlavorFBS) {
2902    NSString *bundleIDNSStr = (NSString *)attach_token;
2903    FBSSystemService *systemService = [[FBSSystemService alloc] init];
2904    pid_t pid = [systemService pidForApplication:bundleIDNSStr];
2905    [systemService release];
2906    if (pid == 0)
2907      return INVALID_NUB_PROCESS;
2908    else
2909      return pid;
2910  }
2911#endif
2912
2913#if defined(WITH_BKS)
2914  if (launch_flavor == eLaunchFlavorBKS) {
2915    NSString *bundleIDNSStr = (NSString *)attach_token;
2916    BKSSystemService *systemService = [[BKSSystemService alloc] init];
2917    pid_t pid = [systemService pidForApplication:bundleIDNSStr];
2918    [systemService release];
2919    if (pid == 0)
2920      return INVALID_NUB_PROCESS;
2921    else
2922      return pid;
2923  }
2924#endif
2925
2926#if defined(WITH_SPRINGBOARD)
2927  if (launch_flavor == eLaunchFlavorSpringBoard) {
2928    CFStringRef bundleIDCFStr = (CFStringRef)attach_token;
2929    Boolean got_it;
2930    nub_process_t attach_pid;
2931    got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid);
2932    if (got_it)
2933      return attach_pid;
2934    else
2935      return INVALID_NUB_PROCESS;
2936  }
2937#endif
2938  return INVALID_NUB_PROCESS;
2939}
2940
2941// Call this to clean up after you have either attached or given up on the
2942// attach.
2943// Pass true for success if you have attached, false if you have not.
2944// The token will also be freed at this point, so you can't use it after calling
2945// this method.
2946
2947void MachProcess::CleanupAfterAttach(const void *attach_token,
2948                                     nub_launch_flavor_t launch_flavor,
2949                                     bool success, DNBError &err_str) {
2950  if (attach_token == NULL)
2951    return;
2952
2953#if defined(WITH_FBS)
2954  if (launch_flavor == eLaunchFlavorFBS) {
2955    if (!success) {
2956      FBSCleanupAfterAttach(attach_token, err_str);
2957    }
2958    CFRelease((CFStringRef)attach_token);
2959  }
2960#endif
2961
2962#if defined(WITH_BKS)
2963
2964  if (launch_flavor == eLaunchFlavorBKS) {
2965    if (!success) {
2966      BKSCleanupAfterAttach(attach_token, err_str);
2967    }
2968    CFRelease((CFStringRef)attach_token);
2969  }
2970#endif
2971
2972#if defined(WITH_SPRINGBOARD)
2973  // Tell SpringBoard to cancel the debug on next launch of this application
2974  // if we failed to attach
2975  if (launch_flavor == eMachProcessFlagsUsingSpringBoard) {
2976    if (!success) {
2977      SBSApplicationLaunchError sbs_error = 0;
2978      CFStringRef bundleIDCFStr = (CFStringRef)attach_token;
2979
2980      sbs_error = SBSLaunchApplicationForDebugging(
2981          bundleIDCFStr, (CFURLRef)NULL, NULL, NULL, NULL, NULL,
2982          SBSApplicationCancelDebugOnNextLaunch);
2983
2984      if (sbs_error != SBSApplicationLaunchErrorSuccess) {
2985        err_str.SetError(sbs_error, DNBError::SpringBoard);
2986        return;
2987      }
2988    }
2989
2990    CFRelease((CFStringRef)attach_token);
2991  }
2992#endif
2993}
2994
2995pid_t MachProcess::LaunchForDebug(
2996    const char *path, char const *argv[], char const *envp[],
2997    const char *working_directory, // NULL => don't change, non-NULL => set
2998                                   // working directory for inferior to this
2999    const char *stdin_path, const char *stdout_path, const char *stderr_path,
3000    bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr,
3001    const char *event_data, DNBError &launch_err) {
3002  // Clear out and clean up from any current state
3003  Clear();
3004
3005  DNBLogThreadedIf(LOG_PROCESS,
3006                   "%s( path = '%s', argv = %p, envp = %p, "
3007                   "launch_flavor = %u, disable_aslr = %d )",
3008                   __FUNCTION__, path, static_cast<const void *>(argv),
3009                   static_cast<const void *>(envp), launch_flavor,
3010                   disable_aslr);
3011
3012  // Fork a child process for debugging
3013  SetState(eStateLaunching);
3014
3015  switch (launch_flavor) {
3016  case eLaunchFlavorForkExec:
3017    m_pid = MachProcess::ForkChildForPTraceDebugging(path, argv, envp, this,
3018                                                     launch_err);
3019    break;
3020#ifdef WITH_FBS
3021  case eLaunchFlavorFBS: {
3022    const char *app_ext = strstr(path, ".app");
3023    if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
3024      std::string app_bundle_path(path, app_ext + strlen(".app"));
3025      m_flags |= (eMachProcessFlagsUsingFBS | eMachProcessFlagsBoardCalculated);
3026      if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
3027                                     no_stdio, disable_aslr, event_data,
3028                                     launch_err) != 0)
3029        return m_pid; // A successful SBLaunchForDebug() returns and assigns a
3030                      // non-zero m_pid.
3031      else
3032        break; // We tried a FBS launch, but didn't succeed lets get out
3033    }
3034  } break;
3035#endif
3036#ifdef WITH_BKS
3037  case eLaunchFlavorBKS: {
3038    const char *app_ext = strstr(path, ".app");
3039    if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
3040      std::string app_bundle_path(path, app_ext + strlen(".app"));
3041      m_flags |= (eMachProcessFlagsUsingBKS | eMachProcessFlagsBoardCalculated);
3042      if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
3043                                     no_stdio, disable_aslr, event_data,
3044                                     launch_err) != 0)
3045        return m_pid; // A successful SBLaunchForDebug() returns and assigns a
3046                      // non-zero m_pid.
3047      else
3048        break; // We tried a BKS launch, but didn't succeed lets get out
3049    }
3050  } break;
3051#endif
3052#ifdef WITH_SPRINGBOARD
3053
3054  case eLaunchFlavorSpringBoard: {
3055    //  .../whatever.app/whatever ?
3056    //  Or .../com.apple.whatever.app/whatever -- be careful of ".app" in
3057    //  "com.apple.whatever" here
3058    const char *app_ext = strstr(path, ".app/");
3059    if (app_ext == NULL) {
3060      // .../whatever.app ?
3061      int len = strlen(path);
3062      if (len > 5) {
3063        if (strcmp(path + len - 4, ".app") == 0) {
3064          app_ext = path + len - 4;
3065        }
3066      }
3067    }
3068    if (app_ext) {
3069      std::string app_bundle_path(path, app_ext + strlen(".app"));
3070      if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio,
3071                           disable_aslr, launch_err) != 0)
3072        return m_pid; // A successful SBLaunchForDebug() returns and assigns a
3073                      // non-zero m_pid.
3074      else
3075        break; // We tried a springboard launch, but didn't succeed lets get out
3076    }
3077  } break;
3078
3079#endif
3080
3081  case eLaunchFlavorPosixSpawn:
3082    m_pid = MachProcess::PosixSpawnChildForPTraceDebugging(
3083        path, DNBArchProtocol::GetArchitecture(), argv, envp, working_directory,
3084        stdin_path, stdout_path, stderr_path, no_stdio, this, disable_aslr,
3085        launch_err);
3086    break;
3087
3088  default:
3089    // Invalid  launch
3090    launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
3091    return INVALID_NUB_PROCESS;
3092  }
3093
3094  if (m_pid == INVALID_NUB_PROCESS) {
3095    // If we don't have a valid process ID and no one has set the error,
3096    // then return a generic error
3097    if (launch_err.Success())
3098      launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
3099  } else {
3100    m_path = path;
3101    size_t i;
3102    char const *arg;
3103    for (i = 0; (arg = argv[i]) != NULL; i++)
3104      m_args.push_back(arg);
3105
3106    m_task.StartExceptionThread(launch_err);
3107    if (launch_err.Fail()) {
3108      if (launch_err.AsString() == NULL)
3109        launch_err.SetErrorString("unable to start the exception thread");
3110      DNBLog("Could not get inferior's Mach exception port, sending ptrace "
3111             "PT_KILL and exiting.");
3112      ::ptrace(PT_KILL, m_pid, 0, 0);
3113      m_pid = INVALID_NUB_PROCESS;
3114      return INVALID_NUB_PROCESS;
3115    }
3116
3117    StartSTDIOThread();
3118
3119    if (launch_flavor == eLaunchFlavorPosixSpawn) {
3120
3121      SetState(eStateAttaching);
3122      errno = 0;
3123      int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
3124      if (err == 0) {
3125        m_flags |= eMachProcessFlagsAttached;
3126        DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid);
3127        launch_err.Clear();
3128      } else {
3129        SetState(eStateExited);
3130        DNBError ptrace_err(errno, DNBError::POSIX);
3131        DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid "
3132                                      "%d (err = %i, errno = %i (%s))",
3133                         m_pid, err, ptrace_err.Status(),
3134                         ptrace_err.AsString());
3135        launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
3136      }
3137    } else {
3138      launch_err.Clear();
3139    }
3140  }
3141  return m_pid;
3142}
3143
3144pid_t MachProcess::PosixSpawnChildForPTraceDebugging(
3145    const char *path, cpu_type_t cpu_type, char const *argv[],
3146    char const *envp[], const char *working_directory, const char *stdin_path,
3147    const char *stdout_path, const char *stderr_path, bool no_stdio,
3148    MachProcess *process, int disable_aslr, DNBError &err) {
3149  posix_spawnattr_t attr;
3150  short flags;
3151  DNBLogThreadedIf(LOG_PROCESS,
3152                   "%s ( path='%s', argv=%p, envp=%p, "
3153                   "working_dir=%s, stdin=%s, stdout=%s "
3154                   "stderr=%s, no-stdio=%i)",
3155                   __FUNCTION__, path, static_cast<const void *>(argv),
3156                   static_cast<const void *>(envp), working_directory,
3157                   stdin_path, stdout_path, stderr_path, no_stdio);
3158
3159  err.SetError(::posix_spawnattr_init(&attr), DNBError::POSIX);
3160  if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3161    err.LogThreaded("::posix_spawnattr_init ( &attr )");
3162  if (err.Fail())
3163    return INVALID_NUB_PROCESS;
3164
3165  flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF |
3166          POSIX_SPAWN_SETSIGMASK;
3167  if (disable_aslr)
3168    flags |= _POSIX_SPAWN_DISABLE_ASLR;
3169
3170  sigset_t no_signals;
3171  sigset_t all_signals;
3172  sigemptyset(&no_signals);
3173  sigfillset(&all_signals);
3174  ::posix_spawnattr_setsigmask(&attr, &no_signals);
3175  ::posix_spawnattr_setsigdefault(&attr, &all_signals);
3176
3177  err.SetError(::posix_spawnattr_setflags(&attr, flags), DNBError::POSIX);
3178  if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3179    err.LogThreaded(
3180        "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )",
3181        flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR"
3182                                          : "");
3183  if (err.Fail())
3184    return INVALID_NUB_PROCESS;
3185
3186// Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail
3187// and we will fail to continue with our process...
3188
3189// On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment....
3190
3191#if !defined(__arm__)
3192
3193  // We don't need to do this for ARM, and we really shouldn't now that we
3194  // have multiple CPU subtypes and no posix_spawnattr call that allows us
3195  // to set which CPU subtype to launch...
3196  if (cpu_type != 0) {
3197    size_t ocount = 0;
3198    err.SetError(::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount),
3199                 DNBError::POSIX);
3200    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3201      err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = "
3202                      "0x%8.8x, count => %llu )",
3203                      cpu_type, (uint64_t)ocount);
3204
3205    if (err.Fail() != 0 || ocount != 1)
3206      return INVALID_NUB_PROCESS;
3207  }
3208#endif
3209
3210  PseudoTerminal pty;
3211
3212  posix_spawn_file_actions_t file_actions;
3213  err.SetError(::posix_spawn_file_actions_init(&file_actions), DNBError::POSIX);
3214  int file_actions_valid = err.Success();
3215  if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS))
3216    err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )");
3217  int pty_error = -1;
3218  pid_t pid = INVALID_NUB_PROCESS;
3219  if (file_actions_valid) {
3220    if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL &&
3221        !no_stdio) {
3222      pty_error = pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
3223      if (pty_error == PseudoTerminal::success) {
3224        stdin_path = stdout_path = stderr_path = pty.SlaveName();
3225      }
3226    }
3227
3228    // if no_stdio or std paths not supplied, then route to "/dev/null".
3229    if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0')
3230      stdin_path = "/dev/null";
3231    if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0')
3232      stdout_path = "/dev/null";
3233    if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0')
3234      stderr_path = "/dev/null";
3235
3236    err.SetError(::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO,
3237                                                    stdin_path,
3238                                                    O_RDONLY | O_NOCTTY, 0),
3239                 DNBError::POSIX);
3240    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3241      err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, "
3242                      "filedes=STDIN_FILENO, path='%s')",
3243                      stdin_path);
3244
3245    err.SetError(::posix_spawn_file_actions_addopen(
3246                     &file_actions, STDOUT_FILENO, stdout_path,
3247                     O_WRONLY | O_NOCTTY | O_CREAT, 0640),
3248                 DNBError::POSIX);
3249    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3250      err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, "
3251                      "filedes=STDOUT_FILENO, path='%s')",
3252                      stdout_path);
3253
3254    err.SetError(::posix_spawn_file_actions_addopen(
3255                     &file_actions, STDERR_FILENO, stderr_path,
3256                     O_WRONLY | O_NOCTTY | O_CREAT, 0640),
3257                 DNBError::POSIX);
3258    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3259      err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, "
3260                      "filedes=STDERR_FILENO, path='%s')",
3261                      stderr_path);
3262
3263    // TODO: Verify if we can set the working directory back immediately
3264    // after the posix_spawnp call without creating a race condition???
3265    if (working_directory)
3266      ::chdir(working_directory);
3267
3268    err.SetError(::posix_spawnp(&pid, path, &file_actions, &attr,
3269                                const_cast<char *const *>(argv),
3270                                const_cast<char *const *>(envp)),
3271                 DNBError::POSIX);
3272    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3273      err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = "
3274                      "%p, attr = %p, argv = %p, envp = %p )",
3275                      pid, path, &file_actions, &attr, argv, envp);
3276  } else {
3277    // TODO: Verify if we can set the working directory back immediately
3278    // after the posix_spawnp call without creating a race condition???
3279    if (working_directory)
3280      ::chdir(working_directory);
3281
3282    err.SetError(::posix_spawnp(&pid, path, NULL, &attr,
3283                                const_cast<char *const *>(argv),
3284                                const_cast<char *const *>(envp)),
3285                 DNBError::POSIX);
3286    if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3287      err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = "
3288                      "%p, attr = %p, argv = %p, envp = %p )",
3289                      pid, path, NULL, &attr, argv, envp);
3290  }
3291
3292  // We have seen some cases where posix_spawnp was returning a valid
3293  // looking pid even when an error was returned, so clear it out
3294  if (err.Fail())
3295    pid = INVALID_NUB_PROCESS;
3296
3297  if (pty_error == 0) {
3298    if (process != NULL) {
3299      int master_fd = pty.ReleaseMasterFD();
3300      process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
3301    }
3302  }
3303  ::posix_spawnattr_destroy(&attr);
3304
3305  if (pid != INVALID_NUB_PROCESS) {
3306    cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess(pid);
3307    DNBLogThreadedIf(LOG_PROCESS,
3308                     "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x",
3309                     __FUNCTION__, pid, pid_cpu_type);
3310    if (pid_cpu_type)
3311      DNBArchProtocol::SetArchitecture(pid_cpu_type);
3312  }
3313
3314  if (file_actions_valid) {
3315    DNBError err2;
3316    err2.SetError(::posix_spawn_file_actions_destroy(&file_actions),
3317                  DNBError::POSIX);
3318    if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
3319      err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )");
3320  }
3321
3322  return pid;
3323}
3324
3325uint32_t MachProcess::GetCPUTypeForLocalProcess(pid_t pid) {
3326  int mib[CTL_MAXNAME] = {
3327      0,
3328  };
3329  size_t len = CTL_MAXNAME;
3330  if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
3331    return 0;
3332
3333  mib[len] = pid;
3334  len++;
3335
3336  cpu_type_t cpu;
3337  size_t cpu_len = sizeof(cpu);
3338  if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
3339    cpu = 0;
3340  return cpu;
3341}
3342
3343pid_t MachProcess::ForkChildForPTraceDebugging(const char *path,
3344                                               char const *argv[],
3345                                               char const *envp[],
3346                                               MachProcess *process,
3347                                               DNBError &launch_err) {
3348  PseudoTerminal::Status pty_error = PseudoTerminal::success;
3349
3350  // Use a fork that ties the child process's stdin/out/err to a pseudo
3351  // terminal so we can read it in our MachProcess::STDIOThread
3352  // as unbuffered io.
3353  PseudoTerminal pty;
3354  pid_t pid = pty.Fork(pty_error);
3355
3356  if (pid < 0) {
3357    //--------------------------------------------------------------
3358    // Status during fork.
3359    //--------------------------------------------------------------
3360    return pid;
3361  } else if (pid == 0) {
3362    //--------------------------------------------------------------
3363    // Child process
3364    //--------------------------------------------------------------
3365    ::ptrace(PT_TRACE_ME, 0, 0, 0); // Debug this process
3366    ::ptrace(PT_SIGEXC, 0, 0, 0);   // Get BSD signals as mach exceptions
3367
3368    // If our parent is setgid, lets make sure we don't inherit those
3369    // extra powers due to nepotism.
3370    if (::setgid(getgid()) == 0) {
3371
3372      // Let the child have its own process group. We need to execute
3373      // this call in both the child and parent to avoid a race condition
3374      // between the two processes.
3375      ::setpgid(0, 0); // Set the child process group to match its pid
3376
3377      // Sleep a bit to before the exec call
3378      ::sleep(1);
3379
3380      // Turn this process into
3381      ::execv(path, const_cast<char *const *>(argv));
3382    }
3383    // Exit with error code. Child process should have taken
3384    // over in above exec call and if the exec fails it will
3385    // exit the child process below.
3386    ::exit(127);
3387  } else {
3388    //--------------------------------------------------------------
3389    // Parent process
3390    //--------------------------------------------------------------
3391    // Let the child have its own process group. We need to execute
3392    // this call in both the child and parent to avoid a race condition
3393    // between the two processes.
3394    ::setpgid(pid, pid); // Set the child process group to match its pid
3395
3396    if (process != NULL) {
3397      // Release our master pty file descriptor so the pty class doesn't
3398      // close it and so we can continue to use it in our STDIO thread
3399      int master_fd = pty.ReleaseMasterFD();
3400      process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
3401    }
3402  }
3403  return pid;
3404}
3405
3406#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS)
3407// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
3408// or NULL if there was some problem getting the bundle id.
3409static CFStringRef CopyBundleIDForPath(const char *app_bundle_path,
3410                                       DNBError &err_str) {
3411  CFBundle bundle(app_bundle_path);
3412  CFStringRef bundleIDCFStr = bundle.GetIdentifier();
3413  std::string bundleID;
3414  if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) {
3415    struct stat app_bundle_stat;
3416    char err_msg[PATH_MAX];
3417
3418    if (::stat(app_bundle_path, &app_bundle_stat) < 0) {
3419      err_str.SetError(errno, DNBError::POSIX);
3420      snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(),
3421               app_bundle_path);
3422      err_str.SetErrorString(err_msg);
3423      DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
3424    } else {
3425      err_str.SetError(-1, DNBError::Generic);
3426      snprintf(err_msg, sizeof(err_msg),
3427               "failed to extract CFBundleIdentifier from %s", app_bundle_path);
3428      err_str.SetErrorString(err_msg);
3429      DNBLogThreadedIf(
3430          LOG_PROCESS,
3431          "%s() error: failed to extract CFBundleIdentifier from '%s'",
3432          __FUNCTION__, app_bundle_path);
3433    }
3434    return NULL;
3435  }
3436
3437  DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s",
3438                   __FUNCTION__, bundleID.c_str());
3439  CFRetain(bundleIDCFStr);
3440
3441  return bundleIDCFStr;
3442}
3443#endif // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined
3444       // (WITH_FBS)
3445#ifdef WITH_SPRINGBOARD
3446
3447pid_t MachProcess::SBLaunchForDebug(const char *path, char const *argv[],
3448                                    char const *envp[], bool no_stdio,
3449                                    bool disable_aslr, DNBError &launch_err) {
3450  // Clear out and clean up from any current state
3451  Clear();
3452
3453  DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
3454
3455  // Fork a child process for debugging
3456  SetState(eStateLaunching);
3457  m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio,
3458                                                     this, launch_err);
3459  if (m_pid != 0) {
3460    m_path = path;
3461    size_t i;
3462    char const *arg;
3463    for (i = 0; (arg = argv[i]) != NULL; i++)
3464      m_args.push_back(arg);
3465    m_task.StartExceptionThread(launch_err);
3466
3467    if (launch_err.Fail()) {
3468      if (launch_err.AsString() == NULL)
3469        launch_err.SetErrorString("unable to start the exception thread");
3470      DNBLog("Could not get inferior's Mach exception port, sending ptrace "
3471             "PT_KILL and exiting.");
3472      ::ptrace(PT_KILL, m_pid, 0, 0);
3473      m_pid = INVALID_NUB_PROCESS;
3474      return INVALID_NUB_PROCESS;
3475    }
3476
3477    StartSTDIOThread();
3478    SetState(eStateAttaching);
3479    int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
3480    if (err == 0) {
3481      m_flags |= eMachProcessFlagsAttached;
3482      DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
3483    } else {
3484      SetState(eStateExited);
3485      DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
3486    }
3487  }
3488  return m_pid;
3489}
3490
3491#include <servers/bootstrap.h>
3492
3493pid_t MachProcess::SBForkChildForPTraceDebugging(
3494    const char *app_bundle_path, char const *argv[], char const *envp[],
3495    bool no_stdio, MachProcess *process, DNBError &launch_err) {
3496  DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__,
3497                   app_bundle_path, process);
3498  CFAllocatorRef alloc = kCFAllocatorDefault;
3499
3500  if (argv[0] == NULL)
3501    return INVALID_NUB_PROCESS;
3502
3503  size_t argc = 0;
3504  // Count the number of arguments
3505  while (argv[argc] != NULL)
3506    argc++;
3507
3508  // Enumerate the arguments
3509  size_t first_launch_arg_idx = 1;
3510  CFReleaser<CFMutableArrayRef> launch_argv;
3511
3512  if (argv[first_launch_arg_idx]) {
3513    size_t launch_argc = argc > 0 ? argc - 1 : 0;
3514    launch_argv.reset(
3515        ::CFArrayCreateMutable(alloc, launch_argc, &kCFTypeArrayCallBacks));
3516    size_t i;
3517    char const *arg;
3518    CFString launch_arg;
3519    for (i = first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL);
3520         i++) {
3521      launch_arg.reset(
3522          ::CFStringCreateWithCString(alloc, arg, kCFStringEncodingUTF8));
3523      if (launch_arg.get() != NULL)
3524        CFArrayAppendValue(launch_argv.get(), launch_arg.get());
3525      else
3526        break;
3527    }
3528  }
3529
3530  // Next fill in the arguments dictionary.  Note, the envp array is of the form
3531  // Variable=value but SpringBoard wants a CF dictionary.  So we have to
3532  // convert
3533  // this here.
3534
3535  CFReleaser<CFMutableDictionaryRef> launch_envp;
3536
3537  if (envp[0]) {
3538    launch_envp.reset(
3539        ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
3540                                    &kCFTypeDictionaryValueCallBacks));
3541    const char *value;
3542    int name_len;
3543    CFString name_string, value_string;
3544
3545    for (int i = 0; envp[i] != NULL; i++) {
3546      value = strstr(envp[i], "=");
3547
3548      // If the name field is empty or there's no =, skip it.  Somebody's
3549      // messing with us.
3550      if (value == NULL || value == envp[i])
3551        continue;
3552
3553      name_len = value - envp[i];
3554
3555      // Now move value over the "="
3556      value++;
3557
3558      name_string.reset(
3559          ::CFStringCreateWithBytes(alloc, (const UInt8 *)envp[i], name_len,
3560                                    kCFStringEncodingUTF8, false));
3561      value_string.reset(
3562          ::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
3563      CFDictionarySetValue(launch_envp.get(), name_string.get(),
3564                           value_string.get());
3565    }
3566  }
3567
3568  CFString stdio_path;
3569
3570  PseudoTerminal pty;
3571  if (!no_stdio) {
3572    PseudoTerminal::Status pty_err =
3573        pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
3574    if (pty_err == PseudoTerminal::success) {
3575      const char *slave_name = pty.SlaveName();
3576      DNBLogThreadedIf(LOG_PROCESS,
3577                       "%s() successfully opened master pty, slave is %s",
3578                       __FUNCTION__, slave_name);
3579      if (slave_name && slave_name[0]) {
3580        ::chmod(slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
3581        stdio_path.SetFileSystemRepresentation(slave_name);
3582      }
3583    }
3584  }
3585
3586  if (stdio_path.get() == NULL) {
3587    stdio_path.SetFileSystemRepresentation("/dev/null");
3588  }
3589
3590  CFStringRef bundleIDCFStr = CopyBundleIDForPath(app_bundle_path, launch_err);
3591  if (bundleIDCFStr == NULL)
3592    return INVALID_NUB_PROCESS;
3593
3594  // This is just for logging:
3595  std::string bundleID;
3596  CFString::UTF8(bundleIDCFStr, bundleID);
3597
3598  DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array",
3599                   __FUNCTION__);
3600
3601  // Find SpringBoard
3602  SBSApplicationLaunchError sbs_error = 0;
3603  sbs_error = SBSLaunchApplicationForDebugging(
3604      bundleIDCFStr,
3605      (CFURLRef)NULL, // openURL
3606      launch_argv.get(),
3607      launch_envp.get(), // CFDictionaryRef environment
3608      stdio_path.get(), stdio_path.get(),
3609      SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
3610
3611  launch_err.SetError(sbs_error, DNBError::SpringBoard);
3612
3613  if (sbs_error == SBSApplicationLaunchErrorSuccess) {
3614    static const useconds_t pid_poll_interval = 200000;
3615    static const useconds_t pid_poll_timeout = 30000000;
3616
3617    useconds_t pid_poll_total = 0;
3618
3619    nub_process_t pid = INVALID_NUB_PROCESS;
3620    Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
3621    // Poll until the process is running, as long as we are getting valid
3622    // responses and the timeout hasn't expired
3623    // A return PID of 0 means the process is not running, which may be because
3624    // it hasn't been (asynchronously) started
3625    // yet, or that it died very quickly (if you weren't using waitForDebugger).
3626    while (!pid_found && pid_poll_total < pid_poll_timeout) {
3627      usleep(pid_poll_interval);
3628      pid_poll_total += pid_poll_interval;
3629      DNBLogThreadedIf(LOG_PROCESS,
3630                       "%s() polling Springboard for pid for %s...",
3631                       __FUNCTION__, bundleID.c_str());
3632      pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
3633    }
3634
3635    CFRelease(bundleIDCFStr);
3636    if (pid_found) {
3637      if (process != NULL) {
3638        // Release our master pty file descriptor so the pty class doesn't
3639        // close it and so we can continue to use it in our STDIO thread
3640        int master_fd = pty.ReleaseMasterFD();
3641        process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
3642      }
3643      DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
3644    } else {
3645      DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.",
3646                  bundleID.c_str());
3647    }
3648    return pid;
3649  }
3650
3651  DNBLogError("unable to launch the application with CFBundleIdentifier '%s' "
3652              "sbs_error = %u",
3653              bundleID.c_str(), sbs_error);
3654  return INVALID_NUB_PROCESS;
3655}
3656
3657#endif // #ifdef WITH_SPRINGBOARD
3658
3659#if defined(WITH_BKS) || defined(WITH_FBS)
3660pid_t MachProcess::BoardServiceLaunchForDebug(
3661    const char *path, char const *argv[], char const *envp[], bool no_stdio,
3662    bool disable_aslr, const char *event_data, DNBError &launch_err) {
3663  DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
3664
3665  // Fork a child process for debugging
3666  SetState(eStateLaunching);
3667  m_pid = BoardServiceForkChildForPTraceDebugging(
3668      path, argv, envp, no_stdio, disable_aslr, event_data, launch_err);
3669  if (m_pid != 0) {
3670    m_path = path;
3671    size_t i;
3672    char const *arg;
3673    for (i = 0; (arg = argv[i]) != NULL; i++)
3674      m_args.push_back(arg);
3675    m_task.StartExceptionThread(launch_err);
3676
3677    if (launch_err.Fail()) {
3678      if (launch_err.AsString() == NULL)
3679        launch_err.SetErrorString("unable to start the exception thread");
3680      DNBLog("Could not get inferior's Mach exception port, sending ptrace "
3681             "PT_KILL and exiting.");
3682      ::ptrace(PT_KILL, m_pid, 0, 0);
3683      m_pid = INVALID_NUB_PROCESS;
3684      return INVALID_NUB_PROCESS;
3685    }
3686
3687    StartSTDIOThread();
3688    SetState(eStateAttaching);
3689    int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
3690    if (err == 0) {
3691      m_flags |= eMachProcessFlagsAttached;
3692      DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
3693    } else {
3694      SetState(eStateExited);
3695      DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
3696    }
3697  }
3698  return m_pid;
3699}
3700
3701pid_t MachProcess::BoardServiceForkChildForPTraceDebugging(
3702    const char *app_bundle_path, char const *argv[], char const *envp[],
3703    bool no_stdio, bool disable_aslr, const char *event_data,
3704    DNBError &launch_err) {
3705  if (argv[0] == NULL)
3706    return INVALID_NUB_PROCESS;
3707
3708  DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__,
3709                   app_bundle_path, this);
3710
3711  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3712
3713  size_t argc = 0;
3714  // Count the number of arguments
3715  while (argv[argc] != NULL)
3716    argc++;
3717
3718  // Enumerate the arguments
3719  size_t first_launch_arg_idx = 1;
3720
3721  NSMutableArray *launch_argv = nil;
3722
3723  if (argv[first_launch_arg_idx]) {
3724    size_t launch_argc = argc > 0 ? argc - 1 : 0;
3725    launch_argv = [NSMutableArray arrayWithCapacity:launch_argc];
3726    size_t i;
3727    char const *arg;
3728    NSString *launch_arg;
3729    for (i = first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL);
3730         i++) {
3731      launch_arg = [NSString stringWithUTF8String:arg];
3732      // FIXME: Should we silently eat an argument that we can't convert into a
3733      // UTF8 string?
3734      if (launch_arg != nil)
3735        [launch_argv addObject:launch_arg];
3736      else
3737        break;
3738    }
3739  }
3740
3741  NSMutableDictionary *launch_envp = nil;
3742  if (envp[0]) {
3743    launch_envp = [[NSMutableDictionary alloc] init];
3744    const char *value;
3745    int name_len;
3746    NSString *name_string, *value_string;
3747
3748    for (int i = 0; envp[i] != NULL; i++) {
3749      value = strstr(envp[i], "=");
3750
3751      // If the name field is empty or there's no =, skip it.  Somebody's
3752      // messing with us.
3753      if (value == NULL || value == envp[i])
3754        continue;
3755
3756      name_len = value - envp[i];
3757
3758      // Now move value over the "="
3759      value++;
3760      name_string = [[NSString alloc] initWithBytes:envp[i]
3761                                             length:name_len
3762                                           encoding:NSUTF8StringEncoding];
3763      value_string = [NSString stringWithUTF8String:value];
3764      [launch_envp setObject:value_string forKey:name_string];
3765    }
3766  }
3767
3768  NSString *stdio_path = nil;
3769  NSFileManager *file_manager = [NSFileManager defaultManager];
3770
3771  PseudoTerminal pty;
3772  if (!no_stdio) {
3773    PseudoTerminal::Status pty_err =
3774        pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
3775    if (pty_err == PseudoTerminal::success) {
3776      const char *slave_name = pty.SlaveName();
3777      DNBLogThreadedIf(LOG_PROCESS,
3778                       "%s() successfully opened master pty, slave is %s",
3779                       __FUNCTION__, slave_name);
3780      if (slave_name && slave_name[0]) {
3781        ::chmod(slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
3782        stdio_path = [file_manager
3783            stringWithFileSystemRepresentation:slave_name
3784                                        length:strlen(slave_name)];
3785      }
3786    }
3787  }
3788
3789  if (stdio_path == nil) {
3790    const char *null_path = "/dev/null";
3791    stdio_path =
3792        [file_manager stringWithFileSystemRepresentation:null_path
3793                                                  length:strlen(null_path)];
3794  }
3795
3796  CFStringRef bundleIDCFStr = CopyBundleIDForPath(app_bundle_path, launch_err);
3797  if (bundleIDCFStr == NULL) {
3798    [pool drain];
3799    return INVALID_NUB_PROCESS;
3800  }
3801
3802  // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use
3803  // toll-free bridging here:
3804  NSString *bundleIDNSStr = (NSString *)bundleIDCFStr;
3805
3806  // Okay, now let's assemble all these goodies into the BackBoardServices
3807  // options mega-dictionary:
3808
3809  NSMutableDictionary *options = nullptr;
3810  pid_t return_pid = INVALID_NUB_PROCESS;
3811  bool success = false;
3812
3813#ifdef WITH_BKS
3814  if (ProcessUsingBackBoard()) {
3815    options =
3816        BKSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp,
3817                                   stdio_path, disable_aslr, event_data);
3818    success = BKSCallOpenApplicationFunction(bundleIDNSStr, options, launch_err,
3819                                             &return_pid);
3820  }
3821#endif
3822#ifdef WITH_FBS
3823  if (ProcessUsingFrontBoard()) {
3824    options =
3825        FBSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp,
3826                                   stdio_path, disable_aslr, event_data);
3827    success = FBSCallOpenApplicationFunction(bundleIDNSStr, options, launch_err,
3828                                             &return_pid);
3829  }
3830#endif
3831
3832  if (success) {
3833    int master_fd = pty.ReleaseMasterFD();
3834    SetChildFileDescriptors(master_fd, master_fd, master_fd);
3835    CFString::UTF8(bundleIDCFStr, m_bundle_id);
3836  }
3837
3838  [pool drain];
3839
3840  return return_pid;
3841}
3842
3843bool MachProcess::BoardServiceSendEvent(const char *event_data,
3844                                        DNBError &send_err) {
3845  bool return_value = true;
3846
3847  if (event_data == NULL || *event_data == '\0') {
3848    DNBLogError("SendEvent called with NULL event data.");
3849    send_err.SetErrorString("SendEvent called with empty event data");
3850    return false;
3851  }
3852
3853  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3854
3855  if (strcmp(event_data, "BackgroundApplication") == 0) {
3856// This is an event I cooked up.  What you actually do is foreground the system
3857// app, so:
3858#ifdef WITH_BKS
3859    if (ProcessUsingBackBoard()) {
3860      return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL);
3861    }
3862#endif
3863#ifdef WITH_FBS
3864    if (ProcessUsingFrontBoard()) {
3865      return_value = FBSCallOpenApplicationFunction(nil, nil, send_err, NULL);
3866    }
3867#endif
3868    if (!return_value) {
3869      DNBLogError("Failed to background application, error: %s.",
3870                  send_err.AsString());
3871    }
3872  } else {
3873    if (m_bundle_id.empty()) {
3874      // See if we can figure out the bundle ID for this PID:
3875
3876      DNBLogError(
3877          "Tried to send event \"%s\" to a process that has no bundle ID.",
3878          event_data);
3879      return false;
3880    }
3881
3882    NSString *bundleIDNSStr =
3883        [NSString stringWithUTF8String:m_bundle_id.c_str()];
3884
3885    NSMutableDictionary *options = [NSMutableDictionary dictionary];
3886
3887#ifdef WITH_BKS
3888    if (ProcessUsingBackBoard()) {
3889      if (!BKSAddEventDataToOptions(options, event_data, send_err)) {
3890        [pool drain];
3891        return false;
3892      }
3893      return_value = BKSCallOpenApplicationFunction(bundleIDNSStr, options,
3894                                                    send_err, NULL);
3895      DNBLogThreadedIf(LOG_PROCESS,
3896                       "Called BKSCallOpenApplicationFunction to send event.");
3897    }
3898#endif
3899#ifdef WITH_FBS
3900    if (ProcessUsingFrontBoard()) {
3901      if (!FBSAddEventDataToOptions(options, event_data, send_err)) {
3902        [pool drain];
3903        return false;
3904      }
3905      return_value = FBSCallOpenApplicationFunction(bundleIDNSStr, options,
3906                                                    send_err, NULL);
3907      DNBLogThreadedIf(LOG_PROCESS,
3908                       "Called FBSCallOpenApplicationFunction to send event.");
3909    }
3910#endif
3911
3912    if (!return_value) {
3913      DNBLogError("Failed to send event: %s, error: %s.", event_data,
3914                  send_err.AsString());
3915    }
3916  }
3917
3918  [pool drain];
3919  return return_value;
3920}
3921#endif // defined(WITH_BKS) || defined (WITH_FBS)
3922
3923#ifdef WITH_BKS
3924void MachProcess::BKSCleanupAfterAttach(const void *attach_token,
3925                                        DNBError &err_str) {
3926  bool success;
3927
3928  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3929
3930  // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use
3931  // toll-free bridging here:
3932  NSString *bundleIDNSStr = (NSString *)attach_token;
3933
3934  // Okay, now let's assemble all these goodies into the BackBoardServices
3935  // options mega-dictionary:
3936
3937  // First we have the debug sub-dictionary:
3938  NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
3939  [debug_options setObject:[NSNumber numberWithBool:YES]
3940                    forKey:BKSDebugOptionKeyCancelDebugOnNextLaunch];
3941
3942  // That will go in the overall dictionary:
3943
3944  NSMutableDictionary *options = [NSMutableDictionary dictionary];
3945  [options setObject:debug_options
3946              forKey:BKSOpenApplicationOptionKeyDebuggingOptions];
3947
3948  success =
3949      BKSCallOpenApplicationFunction(bundleIDNSStr, options, err_str, NULL);
3950
3951  if (!success) {
3952    DNBLogError("error trying to cancel debug on next launch for %s: %s",
3953                [bundleIDNSStr UTF8String], err_str.AsString());
3954  }
3955
3956  [pool drain];
3957}
3958#endif // WITH_BKS
3959
3960#ifdef WITH_FBS
3961void MachProcess::FBSCleanupAfterAttach(const void *attach_token,
3962                                        DNBError &err_str) {
3963  bool success;
3964
3965  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3966
3967  // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use
3968  // toll-free bridging here:
3969  NSString *bundleIDNSStr = (NSString *)attach_token;
3970
3971  // Okay, now let's assemble all these goodies into the BackBoardServices
3972  // options mega-dictionary:
3973
3974  // First we have the debug sub-dictionary:
3975  NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
3976  [debug_options setObject:[NSNumber numberWithBool:YES]
3977                    forKey:FBSDebugOptionKeyCancelDebugOnNextLaunch];
3978
3979  // That will go in the overall dictionary:
3980
3981  NSMutableDictionary *options = [NSMutableDictionary dictionary];
3982  [options setObject:debug_options
3983              forKey:FBSOpenApplicationOptionKeyDebuggingOptions];
3984
3985  success =
3986      FBSCallOpenApplicationFunction(bundleIDNSStr, options, err_str, NULL);
3987
3988  if (!success) {
3989    DNBLogError("error trying to cancel debug on next launch for %s: %s",
3990                [bundleIDNSStr UTF8String], err_str.AsString());
3991  }
3992
3993  [pool drain];
3994}
3995#endif // WITH_FBS
3996
3997
3998void MachProcess::CalculateBoardStatus()
3999{
4000  if (m_flags & eMachProcessFlagsBoardCalculated)
4001    return;
4002  if (m_pid == 0)
4003    return;
4004
4005#if defined (WITH_FBS) || defined (WITH_BKS)
4006    bool found_app_flavor = false;
4007#endif
4008
4009#if defined(WITH_FBS)
4010    if (!found_app_flavor && IsFBSProcess(m_pid)) {
4011      found_app_flavor = true;
4012      m_flags |= eMachProcessFlagsUsingFBS;
4013    }
4014#endif
4015#if defined(WITH_BKS)
4016    if (!found_app_flavor && IsBKSProcess(m_pid)) {
4017      found_app_flavor = true;
4018      m_flags |= eMachProcessFlagsUsingBKS;
4019    }
4020#endif
4021
4022    m_flags |= eMachProcessFlagsBoardCalculated;
4023}
4024
4025bool MachProcess::ProcessUsingBackBoard() {
4026  CalculateBoardStatus();
4027  return (m_flags & eMachProcessFlagsUsingBKS) != 0;
4028}
4029
4030bool MachProcess::ProcessUsingFrontBoard() {
4031  CalculateBoardStatus();
4032  return (m_flags & eMachProcessFlagsUsingFBS) != 0;
4033}
4034