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