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