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