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