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