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