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