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