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