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