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