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