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