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