llvm-project/lldb/tools/debugserver/source/MacOSX/MachProcess.mm

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