MachProcess.cpp revision 72e1c782ba1e4226da37af4722af608de9f39408
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 <mach/mach.h> 16#include <spawn.h> 17#include <sys/fcntl.h> 18#include <sys/types.h> 19#include <sys/ptrace.h> 20#include <sys/stat.h> 21#include <unistd.h> 22#include "MacOSX/CFUtils.h" 23#include "SysSignal.h" 24 25#include <algorithm> 26#include <map> 27 28#include "DNBDataRef.h" 29#include "DNBLog.h" 30#include "DNBThreadResumeActions.h" 31#include "DNBTimer.h" 32#include "MachProcess.h" 33#include "PseudoTerminal.h" 34 35#include "CFBundle.h" 36#include "CFData.h" 37#include "CFString.h" 38 39static CFStringRef CopyBundleIDForPath (const char *app_buncle_path, DNBError &err_str); 40 41#if defined (__arm__) 42 43#include <CoreFoundation/CoreFoundation.h> 44#include <SpringBoardServices/SpringBoardServer.h> 45#include <SpringBoardServices/SBSWatchdogAssertion.h> 46 47 48static bool 49IsSBProcess (nub_process_t pid) 50{ 51 bool opt_runningApps = true; 52 bool opt_debuggable = false; 53 54 CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); 55 if (sbsAppIDs.get() != NULL) 56 { 57 CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); 58 CFIndex i = 0; 59 for (i = 0; i < count; i++) 60 { 61 CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); 62 63 // Get the process id for the app (if there is one) 64 pid_t sbs_pid = INVALID_NUB_PROCESS; 65 if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE) 66 { 67 if (sbs_pid == pid) 68 return true; 69 } 70 } 71 } 72 return false; 73} 74 75 76#endif 77 78#if 0 79#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) 80#else 81#define DEBUG_LOG(fmt, ...) 82#endif 83 84#ifndef MACH_PROCESS_USE_POSIX_SPAWN 85#define MACH_PROCESS_USE_POSIX_SPAWN 1 86#endif 87 88#ifndef _POSIX_SPAWN_DISABLE_ASLR 89#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 90#endif 91 92MachProcess::MachProcess() : 93 m_pid (0), 94 m_child_stdin (-1), 95 m_child_stdout (-1), 96 m_child_stderr (-1), 97 m_path (), 98 m_args (), 99 m_task (this), 100 m_flags (eMachProcessFlagsNone), 101 m_stdio_thread (0), 102 m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE), 103 m_stdout_data (), 104 m_thread_actions (), 105 m_thread_list (), 106 m_exception_messages (), 107 m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE), 108 m_state (eStateUnloaded), 109 m_state_mutex (PTHREAD_MUTEX_RECURSIVE), 110 m_events (0, kAllEventsMask), 111 m_breakpoints (), 112 m_watchpoints (), 113 m_name_to_addr_callback(NULL), 114 m_name_to_addr_baton(NULL), 115 m_image_infos_callback(NULL), 116 m_image_infos_baton(NULL) 117{ 118 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 119} 120 121MachProcess::~MachProcess() 122{ 123 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 124 Clear(); 125} 126 127pid_t 128MachProcess::SetProcessID(pid_t pid) 129{ 130 // Free any previous process specific data or resources 131 Clear(); 132 // Set the current PID appropriately 133 if (pid == 0) 134 m_pid = ::getpid (); 135 else 136 m_pid = pid; 137 return m_pid; // Return actualy PID in case a zero pid was passed in 138} 139 140nub_state_t 141MachProcess::GetState() 142{ 143 // If any other threads access this we will need a mutex for it 144 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 145 return m_state; 146} 147 148const char * 149MachProcess::ThreadGetName(nub_thread_t tid) 150{ 151 return m_thread_list.GetName(tid); 152} 153 154nub_state_t 155MachProcess::ThreadGetState(nub_thread_t tid) 156{ 157 return m_thread_list.GetState(tid); 158} 159 160 161nub_size_t 162MachProcess::GetNumThreads () const 163{ 164 return m_thread_list.NumThreads(); 165} 166 167nub_thread_t 168MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const 169{ 170 return m_thread_list.ThreadIDAtIndex(thread_idx); 171} 172 173nub_thread_t 174MachProcess::GetCurrentThread () 175{ 176 return m_thread_list.CurrentThreadID(); 177} 178 179nub_thread_t 180MachProcess::SetCurrentThread(nub_thread_t tid) 181{ 182 return m_thread_list.SetCurrentThread(tid); 183} 184 185bool 186MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const 187{ 188 return m_thread_list.GetThreadStoppedReason(tid, stop_info); 189} 190 191void 192MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const 193{ 194 return m_thread_list.DumpThreadStoppedReason(tid); 195} 196 197const char * 198MachProcess::GetThreadInfo(nub_thread_t tid) const 199{ 200 return m_thread_list.GetThreadInfo(tid); 201} 202 203const DNBRegisterSetInfo * 204MachProcess::GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const 205{ 206 MachThreadSP thread_sp (m_thread_list.GetThreadByID (tid)); 207 if (thread_sp) 208 { 209 DNBArchProtocol *arch = thread_sp->GetArchProtocol(); 210 if (arch) 211 return arch->GetRegisterSetInfo (num_reg_sets); 212 } 213 *num_reg_sets = 0; 214 return NULL; 215} 216 217bool 218MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const 219{ 220 return m_thread_list.GetRegisterValue(tid, set, reg, value); 221} 222 223bool 224MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const 225{ 226 return m_thread_list.SetRegisterValue(tid, set, reg, value); 227} 228 229void 230MachProcess::SetState(nub_state_t new_state) 231{ 232 // If any other threads access this we will need a mutex for it 233 uint32_t event_mask = 0; 234 235 // Scope for mutex locker 236 { 237 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 238 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( %s )", DNBStateAsString(new_state)); 239 240 const nub_state_t old_state = m_state; 241 242 if (old_state != new_state) 243 { 244 if (NUB_STATE_IS_STOPPED(new_state)) 245 event_mask = eEventProcessStoppedStateChanged; 246 else 247 event_mask = eEventProcessRunningStateChanged; 248 249 m_state = new_state; 250 if (new_state == eStateStopped) 251 m_stop_count++; 252 } 253 } 254 255 if (event_mask != 0) 256 { 257 m_events.SetEvents (event_mask); 258 259 // Wait for the event bit to reset if a reset ACK is requested 260 m_events.WaitForResetAck(event_mask); 261 } 262 263} 264 265void 266MachProcess::Clear() 267{ 268 // Clear any cached thread list while the pid and task are still valid 269 270 m_task.Clear(); 271 // Now clear out all member variables 272 m_pid = INVALID_NUB_PROCESS; 273 CloseChildFileDescriptors(); 274 m_path.clear(); 275 m_args.clear(); 276 SetState(eStateUnloaded); 277 m_flags = eMachProcessFlagsNone; 278 m_stop_count = 0; 279 m_thread_list.Clear(); 280 { 281 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 282 m_exception_messages.clear(); 283 } 284} 285 286 287bool 288MachProcess::StartSTDIOThread() 289{ 290 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 291 // Create the thread that watches for the child STDIO 292 return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0; 293} 294 295 296nub_addr_t 297MachProcess::LookupSymbol(const char *name, const char *shlib) 298{ 299 if (m_name_to_addr_callback != NULL && name && name[0]) 300 return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton); 301 return INVALID_NUB_ADDRESS; 302} 303 304bool 305MachProcess::Resume (const DNBThreadResumeActions& thread_actions) 306{ 307 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()"); 308 nub_state_t state = GetState(); 309 310 if (CanResume(state)) 311 { 312 m_thread_actions = thread_actions; 313 PrivateResume(); 314 return true; 315 } 316 else if (state == eStateRunning) 317 { 318 DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort()); 319 return true; 320 } 321 DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort()); 322 return false; 323} 324 325bool 326MachProcess::Kill (const struct timespec *timeout_abstime) 327{ 328 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()"); 329 nub_state_t state = DoSIGSTOP(true); 330 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state)); 331 errno = 0; 332 ::ptrace (PT_KILL, m_pid, 0, 0); 333 DNBError err; 334 err.SetErrorToErrno(); 335 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", err.Error(), err.AsString()); 336 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 337 PrivateResume (); 338 return true; 339} 340 341bool 342MachProcess::Signal (int signal, const struct timespec *timeout_abstime) 343{ 344 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime); 345 nub_state_t state = GetState(); 346 if (::kill (ProcessID(), signal) == 0) 347 { 348 // If we were running and we have a timeout, wait for the signal to stop 349 if (IsRunning(state) && timeout_abstime) 350 { 351 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime); 352 m_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime); 353 state = GetState(); 354 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state)); 355 return !IsRunning (state); 356 } 357 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime); 358 return true; 359 } 360 DNBError err(errno, DNBError::POSIX); 361 err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal); 362 return false; 363 364} 365 366nub_state_t 367MachProcess::DoSIGSTOP (bool clear_bps_and_wps, uint32_t *thread_idx_ptr) 368{ 369 nub_state_t state = GetState(); 370 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state)); 371 372 if (!IsRunning(state)) 373 { 374 if (clear_bps_and_wps) 375 { 376 DisableAllBreakpoints (true); 377 DisableAllWatchpoints (true); 378 clear_bps_and_wps = false; 379 } 380 381 // If we already have a thread stopped due to a SIGSTOP, we don't have 382 // to do anything... 383 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 384 if (thread_idx_ptr) 385 *thread_idx_ptr = thread_idx; 386 if (thread_idx != UINT32_MAX) 387 return GetState(); 388 389 // No threads were stopped with a SIGSTOP, we need to run and halt the 390 // process with a signal 391 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state)); 392 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 393 PrivateResume (); 394 395 // Reset the event that says we were indeed running 396 m_events.ResetEvents(eEventProcessRunningStateChanged); 397 state = GetState(); 398 } 399 400 // We need to be stopped in order to be able to detach, so we need 401 // to send ourselves a SIGSTOP 402 403 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state)); 404 struct timespec sigstop_timeout; 405 DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0); 406 Signal (SIGSTOP, &sigstop_timeout); 407 if (clear_bps_and_wps) 408 { 409 DisableAllBreakpoints (true); 410 DisableAllWatchpoints (true); 411 clear_bps_and_wps = false; 412 } 413 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 414 if (thread_idx_ptr) 415 *thread_idx_ptr = thread_idx; 416 return GetState(); 417} 418 419bool 420MachProcess::Detach() 421{ 422 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()"); 423 424 uint32_t thread_idx = UINT32_MAX; 425 nub_state_t state = DoSIGSTOP(true, &thread_idx); 426 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state)); 427 428 { 429 m_thread_actions.Clear(); 430 DNBThreadResumeAction thread_action; 431 thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx); 432 thread_action.state = eStateRunning; 433 thread_action.signal = -1; 434 thread_action.addr = INVALID_NUB_ADDRESS; 435 436 m_thread_actions.Append (thread_action); 437 m_thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0); 438 439 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 440 441 ReplyToAllExceptions (); 442 443 } 444 445 m_task.ShutDownExcecptionThread(); 446 447 // Detach from our process 448 errno = 0; 449 nub_process_t pid = m_pid; 450 int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); 451 DNBError err(errno, DNBError::POSIX); 452 if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0)) 453 err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); 454 455 // Resume our task 456 m_task.Resume(); 457 458 // NULL our task out as we have already retored all exception ports 459 m_task.Clear(); 460 461 // Clear out any notion of the process we once were 462 Clear(); 463 464 SetState(eStateDetached); 465 466 return true; 467} 468 469 470nub_size_t 471MachProcess::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const 472{ 473 nub_size_t bytes_removed = 0; 474 const DNBBreakpoint *bp; 475 nub_addr_t intersect_addr; 476 nub_size_t intersect_size; 477 nub_size_t opcode_offset; 478 nub_size_t idx; 479 for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx) 480 { 481 if (bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)) 482 { 483 assert(addr <= intersect_addr && intersect_addr < addr + size); 484 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); 485 assert(opcode_offset + intersect_size <= bp->ByteSize()); 486 nub_size_t buf_offset = intersect_addr - addr; 487 ::memcpy(buf + buf_offset, bp->SavedOpcodeBytes() + opcode_offset, intersect_size); 488 } 489 } 490 return bytes_removed; 491} 492 493//---------------------------------------------------------------------- 494// ReadMemory from the MachProcess level will always remove any software 495// breakpoints from the memory buffer before returning. If you wish to 496// read memory and see those traps, read from the MachTask 497// (m_task.ReadMemory()) as that version will give you what is actually 498// in inferior memory. 499//---------------------------------------------------------------------- 500nub_size_t 501MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf) 502{ 503 // We need to remove any current software traps (enabled software 504 // breakpoints) that we may have placed in our tasks memory. 505 506 // First just read the memory as is 507 nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf); 508 509 // Then place any opcodes that fall into this range back into the buffer 510 // before we return this to callers. 511 if (bytes_read > 0) 512 RemoveTrapsFromBuffer (addr, size, (uint8_t *)buf); 513 return bytes_read; 514} 515 516//---------------------------------------------------------------------- 517// WriteMemory from the MachProcess level will always write memory around 518// any software breakpoints. Any software breakpoints will have their 519// opcodes modified if they are enabled. Any memory that doesn't overlap 520// with software breakpoints will be written to. If you wish to write to 521// inferior memory without this interference, then write to the MachTask 522// (m_task.WriteMemory()) as that version will always modify inferior 523// memory. 524//---------------------------------------------------------------------- 525nub_size_t 526MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) 527{ 528 // We need to write any data that would go where any current software traps 529 // (enabled software breakpoints) any software traps (breakpoints) that we 530 // may have placed in our tasks memory. 531 532 std::map<nub_addr_t, DNBBreakpoint *> addr_to_bp_map; 533 DNBBreakpoint *bp; 534 nub_size_t idx; 535 for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx) 536 { 537 if (bp->IntersectsRange(addr, size, NULL, NULL, NULL)) 538 addr_to_bp_map[bp->Address()] = bp; 539 } 540 541 // If we don't have any software breakpoints that are in this buffer, then 542 // we can just write memory and be done with it. 543 if (addr_to_bp_map.empty()) 544 return m_task.WriteMemory(addr, size, buf); 545 546 // If we make it here, we have some breakpoints that overlap and we need 547 // to work around them. 548 549 nub_size_t bytes_written = 0; 550 nub_addr_t intersect_addr; 551 nub_size_t intersect_size; 552 nub_size_t opcode_offset; 553 const uint8_t *ubuf = (const uint8_t *)buf; 554 std::map<nub_addr_t, DNBBreakpoint *>::iterator pos, end = addr_to_bp_map.end(); 555 for (pos = addr_to_bp_map.begin(); pos != end; ++pos) 556 { 557 bp = pos->second; 558 559 assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)); 560 assert(addr <= intersect_addr && intersect_addr < addr + size); 561 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); 562 assert(opcode_offset + intersect_size <= bp->ByteSize()); 563 564 // Check for bytes before this breakpoint 565 const nub_addr_t curr_addr = addr + bytes_written; 566 if (intersect_addr > curr_addr) 567 { 568 // There are some bytes before this breakpoint that we need to 569 // just write to memory 570 nub_size_t curr_size = intersect_addr - curr_addr; 571 nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written); 572 bytes_written += curr_bytes_written; 573 if (curr_bytes_written != curr_size) 574 { 575 // We weren't able to write all of the requested bytes, we 576 // are done looping and will return the number of bytes that 577 // we have written so far. 578 break; 579 } 580 } 581 582 // Now write any bytes that would cover up any software breakpoints 583 // directly into the breakpoint opcode buffer 584 ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); 585 bytes_written += intersect_size; 586 } 587 588 // Write any remaining bytes after the last breakpoint if we have any left 589 if (bytes_written < size) 590 bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written); 591 592 return bytes_written; 593} 594 595 596void 597MachProcess::ReplyToAllExceptions () 598{ 599 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 600 if (m_exception_messages.empty() == false) 601 { 602 MachException::Message::iterator pos; 603 MachException::Message::iterator begin = m_exception_messages.begin(); 604 MachException::Message::iterator end = m_exception_messages.end(); 605 for (pos = begin; pos != end; ++pos) 606 { 607 DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %d...", std::distance(begin, pos)); 608 int thread_reply_signal = 0; 609 610 const DNBThreadResumeAction *action = m_thread_actions.GetActionForThread (pos->state.thread_port, false); 611 612 if (action) 613 { 614 thread_reply_signal = action->signal; 615 if (thread_reply_signal) 616 m_thread_actions.SetSignalHandledForThread (pos->state.thread_port); 617 } 618 619 DNBError err (pos->Reply(this, thread_reply_signal)); 620 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 621 err.LogThreadedIfError("Error replying to exception"); 622 } 623 624 // Erase all exception message as we should have used and replied 625 // to them all already. 626 m_exception_messages.clear(); 627 } 628} 629void 630MachProcess::PrivateResume () 631{ 632 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 633 634 ReplyToAllExceptions (); 635// bool stepOverBreakInstruction = step; 636 637 // Let the thread prepare to resume and see if any threads want us to 638 // step over a breakpoint instruction (ProcessWillResume will modify 639 // the value of stepOverBreakInstruction). 640 m_thread_list.ProcessWillResume (this, m_thread_actions); 641 642 // Set our state accordingly 643 if (m_thread_actions.NumActionsWithState(eStateStepping)) 644 SetState (eStateStepping); 645 else 646 SetState (eStateRunning); 647 648 // Now resume our task. 649 m_task.Resume(); 650} 651 652nub_break_t 653MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware, thread_t tid) 654{ 655 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, hardware, tid); 656 if (hardware && tid == INVALID_NUB_THREAD) 657 tid = GetCurrentThread(); 658 659 DNBBreakpoint bp(addr, length, tid, hardware); 660 nub_break_t breakID = m_breakpoints.Add(bp); 661 if (EnableBreakpoint(breakID)) 662 { 663 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%4.4x ) => %u", (uint64_t)addr, length, tid, breakID); 664 return breakID; 665 } 666 else 667 { 668 m_breakpoints.Remove(breakID); 669 } 670 // We failed to enable the breakpoint 671 return INVALID_NUB_BREAK_ID; 672} 673 674nub_watch_t 675MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware, thread_t tid) 676{ 677 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, flags = 0x%8.8x, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, watch_flags, hardware, tid); 678 if (hardware && tid == INVALID_NUB_THREAD) 679 tid = GetCurrentThread(); 680 681 DNBBreakpoint watch(addr, length, tid, hardware); 682 watch.SetIsWatchpoint(watch_flags); 683 684 nub_watch_t watchID = m_watchpoints.Add(watch); 685 if (EnableWatchpoint(watchID)) 686 { 687 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => %u", (uint64_t)addr, length, tid, watchID); 688 return watchID; 689 } 690 else 691 { 692 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => FAILED (%u)", (uint64_t)addr, length, tid, watchID); 693 m_watchpoints.Remove(watchID); 694 } 695 // We failed to enable the watchpoint 696 return INVALID_NUB_BREAK_ID; 697} 698 699nub_size_t 700MachProcess::DisableAllBreakpoints(bool remove) 701{ 702 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 703 DNBBreakpoint *bp; 704 nub_size_t disabled_count = 0; 705 nub_size_t idx = 0; 706 while ((bp = m_breakpoints.GetByIndex(idx)) != NULL) 707 { 708 bool success = DisableBreakpoint(bp->GetID(), remove); 709 710 if (success) 711 disabled_count++; 712 // If we failed to disable the breakpoint or we aren't removing the breakpoint 713 // increment the breakpoint index. Otherwise DisableBreakpoint will have removed 714 // the breakpoint at this index and we don't need to change it. 715 if ((success == false) || (remove == false)) 716 idx++; 717 } 718 return disabled_count; 719} 720 721nub_size_t 722MachProcess::DisableAllWatchpoints(bool remove) 723{ 724 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 725 DNBBreakpoint *wp; 726 nub_size_t disabled_count = 0; 727 nub_size_t idx = 0; 728 while ((wp = m_watchpoints.GetByIndex(idx)) != NULL) 729 { 730 bool success = DisableWatchpoint(wp->GetID(), remove); 731 732 if (success) 733 disabled_count++; 734 // If we failed to disable the watchpoint or we aren't removing the watchpoint 735 // increment the watchpoint index. Otherwise DisableWatchpoint will have removed 736 // the watchpoint at this index and we don't need to change it. 737 if ((success == false) || (remove == false)) 738 idx++; 739 } 740 return disabled_count; 741} 742 743bool 744MachProcess::DisableBreakpoint(nub_break_t breakID, bool remove) 745{ 746 DNBBreakpoint *bp = m_breakpoints.FindByID (breakID); 747 if (bp) 748 { 749 nub_addr_t addr = bp->Address(); 750 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx", breakID, remove, (uint64_t)addr); 751 752 if (bp->IsHardware()) 753 { 754 bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp); 755 756 if (hw_disable_result == true) 757 { 758 bp->SetEnabled(false); 759 // Let the thread list know that a breakpoint has been modified 760 if (remove) 761 { 762 m_thread_list.NotifyBreakpointChanged(bp); 763 m_breakpoints.Remove(breakID); 764 } 765 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", breakID, remove, (uint64_t)addr); 766 return true; 767 } 768 769 return false; 770 } 771 772 const nub_size_t break_op_size = bp->ByteSize(); 773 assert (break_op_size > 0); 774 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (bp->ByteSize()); 775 if (break_op_size > 0) 776 { 777 // Clear a software breakoint instruction 778 uint8_t curr_break_op[break_op_size]; 779 bool break_op_found = false; 780 781 // Read the breakpoint opcode 782 if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size) 783 { 784 bool verify = false; 785 if (bp->IsEnabled()) 786 { 787 // Make sure we have the a breakpoint opcode exists at this address 788 if (memcmp(curr_break_op, break_op, break_op_size) == 0) 789 { 790 break_op_found = true; 791 // We found a valid breakpoint opcode at this address, now restore 792 // the saved opcode. 793 if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 794 { 795 verify = true; 796 } 797 else 798 { 799 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx memory write failed when restoring original opcode", breakID, remove, (uint64_t)addr); 800 } 801 } 802 else 803 { 804 DNBLogWarning("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx expected a breakpoint opcode but didn't find one.", breakID, remove, (uint64_t)addr); 805 // Set verify to true and so we can check if the original opcode has already been restored 806 verify = true; 807 } 808 } 809 else 810 { 811 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x$8.8llx is not enabled", breakID, remove, (uint64_t)addr); 812 // Set verify to true and so we can check if the original opcode is there 813 verify = true; 814 } 815 816 if (verify) 817 { 818 uint8_t verify_opcode[break_op_size]; 819 // Verify that our original opcode made it back to the inferior 820 if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size) 821 { 822 // compare the memory we just read with the original opcode 823 if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0) 824 { 825 // SUCCESS 826 bp->SetEnabled(false); 827 // Let the thread list know that a breakpoint has been modified 828 if (remove) 829 { 830 m_thread_list.NotifyBreakpointChanged(bp); 831 m_breakpoints.Remove(breakID); 832 } 833 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx => success", breakID, remove, (uint64_t)addr); 834 return true; 835 } 836 else 837 { 838 if (break_op_found) 839 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: failed to restore original opcode", breakID, remove, (uint64_t)addr); 840 else 841 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: opcode changed", breakID, remove, (uint64_t)addr); 842 } 843 } 844 else 845 { 846 DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr); 847 } 848 } 849 } 850 else 851 { 852 DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr); 853 } 854 } 855 } 856 else 857 { 858 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) invalid breakpoint ID", breakID, remove); 859 } 860 return false; 861} 862 863bool 864MachProcess::DisableWatchpoint(nub_watch_t watchID, bool remove) 865{ 866 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(watchID = %d, remove = %d)", __FUNCTION__, watchID, remove); 867 DNBBreakpoint *wp = m_watchpoints.FindByID (watchID); 868 if (wp) 869 { 870 nub_addr_t addr = wp->Address(); 871 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx", watchID, remove, (uint64_t)addr); 872 873 if (wp->IsHardware()) 874 { 875 bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp); 876 877 if (hw_disable_result == true) 878 { 879 wp->SetEnabled(false); 880 if (remove) 881 m_watchpoints.Remove(watchID); 882 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", watchID, remove, (uint64_t)addr); 883 return true; 884 } 885 } 886 887 // TODO: clear software watchpoints if we implement them 888 } 889 else 890 { 891 DNBLogError("MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) invalid watchpoint ID", watchID, remove); 892 } 893 return false; 894} 895 896 897void 898MachProcess::DumpBreakpoint(nub_break_t breakID) const 899{ 900 DNBLogThreaded("MachProcess::DumpBreakpoint(breakID = %d)", breakID); 901 902 if (NUB_BREAK_ID_IS_VALID(breakID)) 903 { 904 const DNBBreakpoint *bp = m_breakpoints.FindByID(breakID); 905 if (bp) 906 bp->Dump(); 907 else 908 DNBLog("MachProcess::DumpBreakpoint(breakID = %d): invalid breakID", breakID); 909 } 910 else 911 { 912 m_breakpoints.Dump(); 913 } 914} 915 916void 917MachProcess::DumpWatchpoint(nub_watch_t watchID) const 918{ 919 DNBLogThreaded("MachProcess::DumpWatchpoint(watchID = %d)", watchID); 920 921 if (NUB_BREAK_ID_IS_VALID(watchID)) 922 { 923 const DNBBreakpoint *wp = m_watchpoints.FindByID(watchID); 924 if (wp) 925 wp->Dump(); 926 else 927 DNBLog("MachProcess::DumpWatchpoint(watchID = %d): invalid watchID", watchID); 928 } 929 else 930 { 931 m_watchpoints.Dump(); 932 } 933} 934 935bool 936MachProcess::EnableBreakpoint(nub_break_t breakID) 937{ 938 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d )", breakID); 939 DNBBreakpoint *bp = m_breakpoints.FindByID (breakID); 940 if (bp) 941 { 942 nub_addr_t addr = bp->Address(); 943 if (bp->IsEnabled()) 944 { 945 DNBLogWarning("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint already enabled.", breakID, (uint64_t)addr); 946 return true; 947 } 948 else 949 { 950 if (bp->HardwarePreferred()) 951 { 952 bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp)); 953 if (bp->IsHardware()) 954 { 955 bp->SetEnabled(true); 956 return true; 957 } 958 } 959 960 const nub_size_t break_op_size = bp->ByteSize(); 961 assert (break_op_size != 0); 962 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (break_op_size); 963 if (break_op_size > 0) 964 { 965 // Save the original opcode by reading it 966 if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 967 { 968 // Write a software breakpoint in place of the original opcode 969 if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size) 970 { 971 uint8_t verify_break_op[4]; 972 if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size) 973 { 974 if (memcmp(break_op, verify_break_op, break_op_size) == 0) 975 { 976 bp->SetEnabled(true); 977 // Let the thread list know that a breakpoint has been modified 978 m_thread_list.NotifyBreakpointChanged(bp); 979 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: SUCCESS.", breakID, (uint64_t)addr); 980 return true; 981 } 982 else 983 { 984 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint opcode verification failed.", breakID, (uint64_t)addr); 985 } 986 } 987 else 988 { 989 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory to verify breakpoint opcode.", breakID, (uint64_t)addr); 990 } 991 } 992 else 993 { 994 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to write breakpoint opcode to memory.", breakID, (uint64_t)addr); 995 } 996 } 997 else 998 { 999 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory at breakpoint address.", breakID, (uint64_t)addr); 1000 } 1001 } 1002 else 1003 { 1004 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) no software breakpoint opcode for current architecture.", breakID); 1005 } 1006 } 1007 } 1008 return false; 1009} 1010 1011bool 1012MachProcess::EnableWatchpoint(nub_watch_t watchID) 1013{ 1014 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(watchID = %d)", watchID); 1015 DNBBreakpoint *wp = m_watchpoints.FindByID (watchID); 1016 if (wp) 1017 { 1018 nub_addr_t addr = wp->Address(); 1019 if (wp->IsEnabled()) 1020 { 1021 DNBLogWarning("MachProcess::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); 1022 return true; 1023 } 1024 else 1025 { 1026 // Currently only try and set hardware watchpoints. 1027 wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp)); 1028 if (wp->IsHardware()) 1029 { 1030 wp->SetEnabled(true); 1031 return true; 1032 } 1033 // TODO: Add software watchpoints by doing page protection tricks. 1034 } 1035 } 1036 return false; 1037} 1038 1039// Called by the exception thread when an exception has been received from 1040// our process. The exception message is completely filled and the exception 1041// data has already been copied. 1042void 1043MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage) 1044{ 1045 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1046 1047 if (m_exception_messages.empty()) 1048 m_task.Suspend(); 1049 1050 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )"); 1051 1052 // Use a locker to automatically unlock our mutex in case of exceptions 1053 // Add the exception to our internal exception stack 1054 m_exception_messages.push_back(exceptionMessage); 1055} 1056 1057void 1058MachProcess::ExceptionMessageBundleComplete() 1059{ 1060 // We have a complete bundle of exceptions for our child process. 1061 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1062 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size()); 1063 if (!m_exception_messages.empty()) 1064 { 1065 // Let all threads recover from stopping and do any clean up based 1066 // on the previous thread state (if any). 1067 m_thread_list.ProcessDidStop(this); 1068 1069 // Let each thread know of any exceptions 1070 task_t task = m_task.TaskPort(); 1071 size_t i; 1072 for (i=0; i<m_exception_messages.size(); ++i) 1073 { 1074 // Let the thread list figure use the MachProcess to forward all exceptions 1075 // on down to each thread. 1076 if (m_exception_messages[i].state.task_port == task) 1077 m_thread_list.NotifyException(m_exception_messages[i].state); 1078 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 1079 m_exception_messages[i].Dump(); 1080 } 1081 1082 if (DNBLogCheckLogBit(LOG_THREAD)) 1083 m_thread_list.Dump(); 1084 1085 bool step_more = false; 1086 if (m_thread_list.ShouldStop(step_more)) 1087 { 1088 // Wait for the eEventProcessRunningStateChanged event to be reset 1089 // before changing state to stopped to avoid race condition with 1090 // very fast start/stops 1091 struct timespec timeout; 1092 //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms 1093 DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms 1094 m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout); 1095 SetState(eStateStopped); 1096 } 1097 else 1098 { 1099 // Resume without checking our current state. 1100 PrivateResume (); 1101 } 1102 } 1103 else 1104 { 1105 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size()); 1106 } 1107} 1108 1109nub_size_t 1110MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed) 1111{ 1112 if (m_image_infos_callback != NULL) 1113 return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton); 1114 return 0; 1115} 1116 1117void 1118MachProcess::SharedLibrariesUpdated ( ) 1119{ 1120 uint32_t event_bits = eEventSharedLibsStateChange; 1121 // Set the shared library event bit to let clients know of shared library 1122 // changes 1123 m_events.SetEvents(event_bits); 1124 // Wait for the event bit to reset if a reset ACK is requested 1125 m_events.WaitForResetAck(event_bits); 1126} 1127 1128void 1129MachProcess::AppendSTDOUT (char* s, size_t len) 1130{ 1131 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%d> %s) ...", __FUNCTION__, len, s); 1132 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1133 m_stdout_data.append(s, len); 1134 m_events.SetEvents(eEventStdioAvailable); 1135 1136 // Wait for the event bit to reset if a reset ACK is requested 1137 m_events.WaitForResetAck(eEventStdioAvailable); 1138} 1139 1140size_t 1141MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size) 1142{ 1143 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); 1144 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1145 size_t bytes_available = m_stdout_data.size(); 1146 if (bytes_available > 0) 1147 { 1148 if (bytes_available > buf_size) 1149 { 1150 memcpy(buf, m_stdout_data.data(), buf_size); 1151 m_stdout_data.erase(0, buf_size); 1152 bytes_available = buf_size; 1153 } 1154 else 1155 { 1156 memcpy(buf, m_stdout_data.data(), bytes_available); 1157 m_stdout_data.clear(); 1158 } 1159 } 1160 return bytes_available; 1161} 1162 1163nub_addr_t 1164MachProcess::GetDYLDAllImageInfosAddress () 1165{ 1166 DNBError err; 1167 return m_task.GetDYLDAllImageInfosAddress(err); 1168} 1169 1170size_t 1171MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size) 1172{ 1173 return 0; 1174} 1175 1176void * 1177MachProcess::STDIOThread(void *arg) 1178{ 1179 MachProcess *proc = (MachProcess*) arg; 1180 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); 1181 1182 // We start use a base and more options so we can control if we 1183 // are currently using a timeout on the mach_msg. We do this to get a 1184 // bunch of related exceptions on our exception port so we can process 1185 // then together. When we have multiple threads, we can get an exception 1186 // per thread and they will come in consecutively. The main thread loop 1187 // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT 1188 // flag set in the options, so we will wait forever for an exception on 1189 // our exception port. After we get one exception, we then will use the 1190 // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current 1191 // exceptions for our process. After we have received the last pending 1192 // exception, we will get a timeout which enables us to then notify 1193 // our main thread that we have an exception bundle avaiable. We then wait 1194 // for the main thread to tell this exception thread to start trying to get 1195 // exceptions messages again and we start again with a mach_msg read with 1196 // infinite timeout. 1197 DNBError err; 1198 int stdout_fd = proc->GetStdoutFileDescriptor(); 1199 int stderr_fd = proc->GetStderrFileDescriptor(); 1200 if (stdout_fd == stderr_fd) 1201 stderr_fd = -1; 1202 1203 while (stdout_fd >= 0 || stderr_fd >= 0) 1204 { 1205 ::pthread_testcancel (); 1206 1207 fd_set read_fds; 1208 FD_ZERO (&read_fds); 1209 if (stdout_fd >= 0) 1210 FD_SET (stdout_fd, &read_fds); 1211 if (stderr_fd >= 0) 1212 FD_SET (stderr_fd, &read_fds); 1213 int nfds = std::max<int>(stdout_fd, stderr_fd) + 1; 1214 1215 int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); 1216 DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1217 1218 if (num_set_fds < 0) 1219 { 1220 int select_errno = errno; 1221 if (DNBLogCheckLogBit(LOG_PROCESS)) 1222 { 1223 err.SetError (select_errno, DNBError::POSIX); 1224 err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1225 } 1226 1227 switch (select_errno) 1228 { 1229 case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO 1230 break; 1231 case EBADF: // One of the descriptor sets specified an invalid descriptor. 1232 return NULL; 1233 break; 1234 case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. 1235 case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. 1236 default: // Other unknown error 1237 break; 1238 } 1239 } 1240 else if (num_set_fds == 0) 1241 { 1242 } 1243 else 1244 { 1245 char s[1024]; 1246 s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination 1247 int bytes_read = 0; 1248 if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) 1249 { 1250 do 1251 { 1252 bytes_read = ::read (stdout_fd, s, sizeof(s)-1); 1253 if (bytes_read < 0) 1254 { 1255 int read_errno = errno; 1256 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1257 } 1258 else if (bytes_read == 0) 1259 { 1260 // EOF... 1261 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); 1262 stdout_fd = -1; 1263 } 1264 else if (bytes_read > 0) 1265 { 1266 proc->AppendSTDOUT(s, bytes_read); 1267 } 1268 1269 } while (bytes_read > 0); 1270 } 1271 1272 if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) 1273 { 1274 do 1275 { 1276 bytes_read = ::read (stderr_fd, s, sizeof(s)-1); 1277 if (bytes_read < 0) 1278 { 1279 int read_errno = errno; 1280 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1281 } 1282 else if (bytes_read == 0) 1283 { 1284 // EOF... 1285 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); 1286 stderr_fd = -1; 1287 } 1288 else if (bytes_read > 0) 1289 { 1290 proc->AppendSTDOUT(s, bytes_read); 1291 } 1292 1293 } while (bytes_read > 0); 1294 } 1295 } 1296 } 1297 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg); 1298 return NULL; 1299} 1300 1301pid_t 1302MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len) 1303{ 1304 // Clear out and clean up from any current state 1305 Clear(); 1306 if (pid != 0) 1307 { 1308 DNBError err; 1309 // Make sure the process exists... 1310 if (::getpgid (pid) < 0) 1311 { 1312 err.SetErrorToErrno(); 1313 const char *err_cstr = err.AsString(); 1314 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process"); 1315 return INVALID_NUB_PROCESS; 1316 } 1317 1318 SetState(eStateAttaching); 1319 m_pid = pid; 1320 // Let ourselves know we are going to be using SBS if the correct flag bit is set... 1321#if defined (__arm__) 1322 if (IsSBProcess(pid)) 1323 m_flags |= eMachProcessFlagsUsingSBS; 1324#endif 1325 if (!m_task.StartExceptionThread(err)) 1326 { 1327 const char *err_cstr = err.AsString(); 1328 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread"); 1329 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1330 m_pid = INVALID_NUB_PROCESS; 1331 return INVALID_NUB_PROCESS; 1332 } 1333 1334 errno = 0; 1335 if (::ptrace (PT_ATTACHEXC, pid, 0, 0)) 1336 err.SetError(errno); 1337 else 1338 err.Clear(); 1339 1340 if (err.Success()) 1341 { 1342 m_flags |= eMachProcessFlagsAttached; 1343 // Sleep a bit to let the exception get received and set our process status 1344 // to stopped. 1345 ::usleep(250000); 1346 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid); 1347 return m_pid; 1348 } 1349 else 1350 { 1351 ::snprintf (err_str, err_len, "%s", err.AsString()); 1352 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1353 } 1354 } 1355 return INVALID_NUB_PROCESS; 1356} 1357 1358// Do the process specific setup for attach. If this returns NULL, then there's no 1359// platform specific stuff to be done to wait for the attach. If you get non-null, 1360// pass that token to the CheckForProcess method, and then to CleanupAfterAttach. 1361 1362// Call PrepareForAttach before attaching to a process that has not yet launched 1363// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach. 1364// You should call CleanupAfterAttach to free the token, and do whatever other 1365// cleanup seems good. 1366 1367const void * 1368MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str) 1369{ 1370#if defined (__arm__) 1371 // Tell SpringBoard to halt the next launch of this application on startup. 1372 1373 if (!waitfor) 1374 return NULL; 1375 1376 const char *app_ext = strstr(path, ".app"); 1377 if (app_ext == NULL) 1378 { 1379 DNBLogThreadedIf(LOG_PROCESS, "%s: path '%s' doesn't contain .app, we can't tell springboard to wait for launch...", path); 1380 return NULL; 1381 } 1382 1383 if (launch_flavor != eLaunchFlavorSpringBoard 1384 && launch_flavor != eLaunchFlavorDefault) 1385 return NULL; 1386 1387 std::string app_bundle_path(path, app_ext + strlen(".app")); 1388 1389 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), err_str); 1390 std::string bundleIDStr; 1391 CFString::UTF8(bundleIDCFStr, bundleIDStr); 1392 DNBLogThreadedIf(LOG_PROCESS, "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", app_bundle_path.c_str (), bundleIDStr.c_str()); 1393 1394 if (bundleIDCFStr == NULL) 1395 { 1396 return NULL; 1397 } 1398 1399 SBSApplicationLaunchError sbs_error = 0; 1400 1401 const char *stdout_err = "/dev/null"; 1402 CFString stdio_path; 1403 stdio_path.SetFileSystemRepresentation (stdout_err); 1404 1405 DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", bundleIDStr.c_str(), stdout_err, stdout_err); 1406 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1407 (CFURLRef)NULL, // openURL 1408 NULL, // launch_argv.get(), 1409 NULL, // launch_envp.get(), // CFDictionaryRef environment 1410 stdio_path.get(), 1411 stdio_path.get(), 1412 SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger); 1413 1414 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1415 { 1416 err_str.SetError(sbs_error, DNBError::SpringBoard); 1417 return NULL; 1418 } 1419 1420 DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); 1421 return bundleIDCFStr; 1422# else 1423 return NULL; 1424#endif 1425} 1426 1427// Pass in the token you got from PrepareForAttach. If there is a process 1428// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS 1429// will be returned. 1430 1431nub_process_t 1432MachProcess::CheckForProcess (const void *attach_token) 1433{ 1434 if (attach_token == NULL) 1435 return INVALID_NUB_PROCESS; 1436 1437#if defined (__arm__) 1438 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1439 Boolean got_it; 1440 nub_process_t attach_pid; 1441 got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid); 1442 if (got_it) 1443 return attach_pid; 1444 else 1445 return INVALID_NUB_PROCESS; 1446#endif 1447 return INVALID_NUB_PROCESS; 1448} 1449 1450// Call this to clean up after you have either attached or given up on the attach. 1451// Pass true for success if you have attached, false if you have not. 1452// The token will also be freed at this point, so you can't use it after calling 1453// this method. 1454 1455void 1456MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str) 1457{ 1458#if defined (__arm__) 1459 if (attach_token == NULL) 1460 return; 1461 1462 // Tell SpringBoard to cancel the debug on next launch of this application 1463 // if we failed to attach 1464 if (!success) 1465 { 1466 SBSApplicationLaunchError sbs_error = 0; 1467 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1468 1469 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1470 (CFURLRef)NULL, 1471 NULL, 1472 NULL, 1473 NULL, 1474 NULL, 1475 SBSApplicationCancelDebugOnNextLaunch); 1476 1477 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1478 { 1479 err_str.SetError(sbs_error, DNBError::SpringBoard); 1480 return; 1481 } 1482 } 1483 1484 CFRelease((CFStringRef) attach_token); 1485#endif 1486} 1487 1488pid_t 1489MachProcess::LaunchForDebug 1490( 1491 const char *path, 1492 char const *argv[], 1493 char const *envp[], 1494 const char *working_directory, // NULL => dont' change, non-NULL => set working directory for inferior to this 1495 const char *stdin_path, 1496 const char *stdout_path, 1497 const char *stderr_path, 1498 bool no_stdio, 1499 nub_launch_flavor_t launch_flavor, 1500 int disable_aslr, 1501 DNBError &launch_err 1502) 1503{ 1504 // Clear out and clean up from any current state 1505 Clear(); 1506 1507 DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr); 1508 1509 // Fork a child process for debugging 1510 SetState(eStateLaunching); 1511 1512 switch (launch_flavor) 1513 { 1514 case eLaunchFlavorForkExec: 1515 m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err); 1516 break; 1517 1518 case eLaunchFlavorPosixSpawn: 1519 m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, 1520 DNBArchProtocol::GetArchitecture (), 1521 argv, 1522 envp, 1523 working_directory, 1524 stdin_path, 1525 stdout_path, 1526 stderr_path, 1527 no_stdio, 1528 this, 1529 disable_aslr, 1530 launch_err); 1531 break; 1532 1533#if defined (__arm__) 1534 1535 case eLaunchFlavorSpringBoard: 1536 { 1537 const char *app_ext = strstr(path, ".app"); 1538 if (app_ext != NULL) 1539 { 1540 std::string app_bundle_path(path, app_ext + strlen(".app")); 1541 return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, launch_err); 1542 } 1543 } 1544 break; 1545 1546#endif 1547 1548 default: 1549 // Invalid launch 1550 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 1551 return INVALID_NUB_PROCESS; 1552 } 1553 1554 if (m_pid == INVALID_NUB_PROCESS) 1555 { 1556 // If we don't have a valid process ID and no one has set the error, 1557 // then return a generic error 1558 if (launch_err.Success()) 1559 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 1560 } 1561 else 1562 { 1563 m_path = path; 1564 size_t i; 1565 char const *arg; 1566 for (i=0; (arg = argv[i]) != NULL; i++) 1567 m_args.push_back(arg); 1568 1569 m_task.StartExceptionThread(launch_err); 1570 if (launch_err.Fail()) 1571 { 1572 if (launch_err.AsString() == NULL) 1573 launch_err.SetErrorString("unable to start the exception thread"); 1574 ::ptrace (PT_KILL, m_pid, 0, 0); 1575 m_pid = INVALID_NUB_PROCESS; 1576 return INVALID_NUB_PROCESS; 1577 } 1578 1579 StartSTDIOThread(); 1580 1581 if (launch_flavor == eLaunchFlavorPosixSpawn) 1582 { 1583 1584 SetState (eStateAttaching); 1585 errno = 0; 1586 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 1587 if (err == 0) 1588 { 1589 m_flags |= eMachProcessFlagsAttached; 1590 DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid); 1591 launch_err.Clear(); 1592 } 1593 else 1594 { 1595 SetState (eStateExited); 1596 DNBError ptrace_err(errno, DNBError::POSIX); 1597 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()); 1598 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 1599 } 1600 } 1601 else 1602 { 1603 launch_err.Clear(); 1604 } 1605 } 1606 return m_pid; 1607} 1608 1609pid_t 1610MachProcess::PosixSpawnChildForPTraceDebugging 1611( 1612 const char *path, 1613 cpu_type_t cpu_type, 1614 char const *argv[], 1615 char const *envp[], 1616 const char *working_directory, 1617 const char *stdin_path, 1618 const char *stdout_path, 1619 const char *stderr_path, 1620 bool no_stdio, 1621 MachProcess* process, 1622 int disable_aslr, 1623 DNBError& err 1624) 1625{ 1626 posix_spawnattr_t attr; 1627 short flags; 1628 DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp); 1629 1630 err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX); 1631 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1632 err.LogThreaded("::posix_spawnattr_init ( &attr )"); 1633 if (err.Fail()) 1634 return INVALID_NUB_PROCESS; 1635 1636 flags = POSIX_SPAWN_START_SUSPENDED; 1637 if (disable_aslr) 1638 flags |= _POSIX_SPAWN_DISABLE_ASLR; 1639 1640 err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX); 1641 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1642 err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : ""); 1643 if (err.Fail()) 1644 return INVALID_NUB_PROCESS; 1645 1646 // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail 1647 // and we will fail to continue with our process... 1648 1649 // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment.... 1650 1651#if !defined(__arm__) 1652 1653 // We don't need to do this for ARM, and we really shouldn't now that we 1654 // have multiple CPU subtypes and no posix_spawnattr call that allows us 1655 // to set which CPU subtype to launch... 1656 size_t ocount = 0; 1657 err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX); 1658 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1659 err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu_type, ocount); 1660 1661 if (err.Fail() != 0 || ocount != 1) 1662 return INVALID_NUB_PROCESS; 1663 1664#endif 1665 1666 PseudoTerminal pty; 1667 1668 posix_spawn_file_actions_t file_actions; 1669 err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX); 1670 int file_actions_valid = err.Success(); 1671 if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS)) 1672 err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )"); 1673 int pty_error = -1; 1674 pid_t pid = INVALID_NUB_PROCESS; 1675 if (file_actions_valid) 1676 { 1677 if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && !no_stdio) 1678 { 1679 pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 1680 if (pty_error == PseudoTerminal::success) 1681 { 1682 stdin_path = stdout_path = stderr_path = pty.SlaveName(); 1683 } 1684 } 1685 1686 // if no_stdio, then do open file actions, opening /dev/null. 1687 if (no_stdio) 1688 { 1689 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, STDIN_FILENO, "/dev/null", 1690 O_RDONLY | O_NOCTTY, 0), DNBError::POSIX); 1691 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 1692 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDIN_FILENO, path=/dev/null)"); 1693 1694 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, STDOUT_FILENO, "/dev/null", 1695 O_WRONLY | O_NOCTTY, 0), DNBError::POSIX); 1696 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 1697 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDOUT_FILENO, path=/dev/null)"); 1698 1699 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, STDERR_FILENO, "/dev/null", 1700 O_RDWR | O_NOCTTY, 0), DNBError::POSIX); 1701 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 1702 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDERR_FILENO, path=/dev/null)"); 1703 } 1704 else 1705 { 1706 int slave_fd_err = open (stderr_path ? stderr_path : "/dev/null", O_RDWR , 0); 1707 int slave_fd_in = open (stdin_path ? stdin_path : "/dev/null", O_RDONLY, 0); 1708 int slave_fd_out = open (stdout_path ? stdout_path : "/dev/null", O_WRONLY, 0); 1709 1710 err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_err, STDERR_FILENO), DNBError::POSIX); 1711 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1712 err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d, newfiledes = STDERR_FILENO )", slave_fd_err); 1713 1714 err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_in, STDIN_FILENO), DNBError::POSIX); 1715 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1716 err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d, newfiledes = STDIN_FILENO )", slave_fd_in); 1717 1718 err.SetError( ::posix_spawn_file_actions_adddup2(&file_actions, slave_fd_out, STDOUT_FILENO), DNBError::POSIX); 1719 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1720 err.LogThreaded("::posix_spawn_file_actions_adddup2 ( &file_actions, filedes = %d, newfiledes = STDOUT_FILENO )", slave_fd_out); 1721 } 1722 err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 1723 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1724 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); 1725 } 1726 else 1727 { 1728 err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 1729 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1730 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); 1731 } 1732 1733 // We have seen some cases where posix_spawnp was returning a valid 1734 // looking pid even when an error was returned, so clear it out 1735 if (err.Fail()) 1736 pid = INVALID_NUB_PROCESS; 1737 1738 if (pty_error == 0) 1739 { 1740 if (process != NULL) 1741 { 1742 int master_fd = pty.ReleaseMasterFD(); 1743 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 1744 } 1745 } 1746 1747 ::posix_spawnattr_destroy (&attr); 1748 1749 if (file_actions_valid) 1750 { 1751 DNBError err2; 1752 err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX); 1753 if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 1754 err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )"); 1755 } 1756 1757 return pid; 1758} 1759 1760pid_t 1761MachProcess::ForkChildForPTraceDebugging 1762( 1763 const char *path, 1764 char const *argv[], 1765 char const *envp[], 1766 MachProcess* process, 1767 DNBError& launch_err 1768) 1769{ 1770 PseudoTerminal::Error pty_error = PseudoTerminal::success; 1771 1772 // Use a fork that ties the child process's stdin/out/err to a pseudo 1773 // terminal so we can read it in our MachProcess::STDIOThread 1774 // as unbuffered io. 1775 PseudoTerminal pty; 1776 pid_t pid = pty.Fork(pty_error); 1777 1778 if (pid < 0) 1779 { 1780 //-------------------------------------------------------------- 1781 // Error during fork. 1782 //-------------------------------------------------------------- 1783 return pid; 1784 } 1785 else if (pid == 0) 1786 { 1787 //-------------------------------------------------------------- 1788 // Child process 1789 //-------------------------------------------------------------- 1790 ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process 1791 ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions 1792 1793 // If our parent is setgid, lets make sure we don't inherit those 1794 // extra powers due to nepotism. 1795 ::setgid (getgid ()); 1796 1797 // Let the child have its own process group. We need to execute 1798 // this call in both the child and parent to avoid a race condition 1799 // between the two processes. 1800 ::setpgid (0, 0); // Set the child process group to match its pid 1801 1802 // Sleep a bit to before the exec call 1803 ::sleep (1); 1804 1805 // Turn this process into 1806 ::execv (path, (char * const *)argv); 1807 // Exit with error code. Child process should have taken 1808 // over in above exec call and if the exec fails it will 1809 // exit the child process below. 1810 ::exit (127); 1811 } 1812 else 1813 { 1814 //-------------------------------------------------------------- 1815 // Parent process 1816 //-------------------------------------------------------------- 1817 // Let the child have its own process group. We need to execute 1818 // this call in both the child and parent to avoid a race condition 1819 // between the two processes. 1820 ::setpgid (pid, pid); // Set the child process group to match its pid 1821 1822 if (process != NULL) 1823 { 1824 // Release our master pty file descriptor so the pty class doesn't 1825 // close it and so we can continue to use it in our STDIO thread 1826 int master_fd = pty.ReleaseMasterFD(); 1827 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 1828 } 1829 } 1830 return pid; 1831} 1832 1833#if defined (__arm__) 1834 1835pid_t 1836MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, DNBError &launch_err) 1837{ 1838 // Clear out and clean up from any current state 1839 Clear(); 1840 1841 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 1842 1843 // Fork a child process for debugging 1844 SetState(eStateLaunching); 1845 m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio, this, launch_err); 1846 if (m_pid != 0) 1847 { 1848 m_flags |= eMachProcessFlagsUsingSBS; 1849 m_path = path; 1850 size_t i; 1851 char const *arg; 1852 for (i=0; (arg = argv[i]) != NULL; i++) 1853 m_args.push_back(arg); 1854 m_task.StartExceptionThread(launch_err); 1855 1856 if (launch_err.Fail()) 1857 { 1858 if (launch_err.AsString() == NULL) 1859 launch_err.SetErrorString("unable to start the exception thread"); 1860 ::ptrace (PT_KILL, m_pid, 0, 0); 1861 m_pid = INVALID_NUB_PROCESS; 1862 return INVALID_NUB_PROCESS; 1863 } 1864 1865 StartSTDIOThread(); 1866 SetState (eStateAttaching); 1867 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 1868 if (err == 0) 1869 { 1870 m_flags |= eMachProcessFlagsAttached; 1871 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); 1872 } 1873 else 1874 { 1875 SetState (eStateExited); 1876 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); 1877 } 1878 } 1879 return m_pid; 1880} 1881 1882#include <servers/bootstrap.h> 1883 1884// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, 1885// or NULL if there was some problem getting the bundle id. 1886static CFStringRef 1887CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str) 1888{ 1889 CFBundle bundle(app_bundle_path); 1890 CFStringRef bundleIDCFStr = bundle.GetIdentifier(); 1891 std::string bundleID; 1892 if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) 1893 { 1894 struct stat app_bundle_stat; 1895 char err_msg[PATH_MAX]; 1896 1897 if (::stat (app_bundle_path, &app_bundle_stat) < 0) 1898 { 1899 err_str.SetError(errno, DNBError::POSIX); 1900 snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path); 1901 err_str.SetErrorString(err_msg); 1902 DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); 1903 } 1904 else 1905 { 1906 err_str.SetError(-1, DNBError::Generic); 1907 snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path); 1908 err_str.SetErrorString(err_msg); 1909 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); 1910 } 1911 return NULL; 1912 } 1913 1914 DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); 1915 CFRetain (bundleIDCFStr); 1916 1917 return bundleIDCFStr; 1918} 1919 1920pid_t 1921MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err) 1922{ 1923 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); 1924 CFAllocatorRef alloc = kCFAllocatorDefault; 1925 1926 if (argv[0] == NULL) 1927 return INVALID_NUB_PROCESS; 1928 1929 size_t argc = 0; 1930 // Count the number of arguments 1931 while (argv[argc] != NULL) 1932 argc++; 1933 1934 // Enumerate the arguments 1935 size_t first_launch_arg_idx = 1; 1936 CFReleaser<CFMutableArrayRef> launch_argv; 1937 1938 if (argv[first_launch_arg_idx]) 1939 { 1940 size_t launch_argc = argc > 0 ? argc - 1 : 0; 1941 launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); 1942 size_t i; 1943 char const *arg; 1944 CFString launch_arg; 1945 for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) 1946 { 1947 launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); 1948 if (launch_arg.get() != NULL) 1949 CFArrayAppendValue(launch_argv.get(), launch_arg.get()); 1950 else 1951 break; 1952 } 1953 } 1954 1955 // Next fill in the arguments dictionary. Note, the envp array is of the form 1956 // Variable=value but SpringBoard wants a CF dictionary. So we have to convert 1957 // this here. 1958 1959 CFReleaser<CFMutableDictionaryRef> launch_envp; 1960 1961 if (envp[0]) 1962 { 1963 launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 1964 const char *value; 1965 int name_len; 1966 CFString name_string, value_string; 1967 1968 for (int i = 0; envp[i] != NULL; i++) 1969 { 1970 value = strstr (envp[i], "="); 1971 1972 // If the name field is empty or there's no =, skip it. Somebody's messing with us. 1973 if (value == NULL || value == envp[i]) 1974 continue; 1975 1976 name_len = value - envp[i]; 1977 1978 // Now move value over the "=" 1979 value++; 1980 1981 name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); 1982 value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); 1983 CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); 1984 } 1985 } 1986 1987 CFString stdio_path; 1988 1989 PseudoTerminal pty; 1990 if (!no_stdio) 1991 { 1992 PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 1993 if (pty_err == PseudoTerminal::success) 1994 { 1995 const char* slave_name = pty.SlaveName(); 1996 DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); 1997 if (slave_name && slave_name[0]) 1998 { 1999 ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); 2000 stdio_path.SetFileSystemRepresentation (slave_name); 2001 } 2002 } 2003 } 2004 2005 if (stdio_path.get() == NULL) 2006 { 2007 stdio_path.SetFileSystemRepresentation ("/dev/null"); 2008 } 2009 2010 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); 2011 if (bundleIDCFStr == NULL) 2012 return INVALID_NUB_PROCESS; 2013 2014 std::string bundleID; 2015 CFString::UTF8(bundleIDCFStr, bundleID); 2016 2017 CFData argv_data(NULL); 2018 2019 if (launch_argv.get()) 2020 { 2021 if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL) 2022 { 2023 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__); 2024 return INVALID_NUB_PROCESS; 2025 } 2026 } 2027 2028 DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); 2029 2030 // Find SpringBoard 2031 SBSApplicationLaunchError sbs_error = 0; 2032 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 2033 (CFURLRef)NULL, // openURL 2034 launch_argv.get(), 2035 launch_envp.get(), // CFDictionaryRef environment 2036 stdio_path.get(), 2037 stdio_path.get(), 2038 SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); 2039 2040 2041 launch_err.SetError(sbs_error, DNBError::SpringBoard); 2042 2043 if (sbs_error == SBSApplicationLaunchErrorSuccess) 2044 { 2045 static const useconds_t pid_poll_interval = 200000; 2046 static const useconds_t pid_poll_timeout = 30000000; 2047 2048 useconds_t pid_poll_total = 0; 2049 2050 nub_process_t pid = INVALID_NUB_PROCESS; 2051 Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2052 // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired 2053 // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started 2054 // yet, or that it died very quickly (if you weren't using waitForDebugger). 2055 while (!pid_found && pid_poll_total < pid_poll_timeout) 2056 { 2057 usleep (pid_poll_interval); 2058 pid_poll_total += pid_poll_interval; 2059 DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); 2060 pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2061 } 2062 2063 CFRelease (bundleIDCFStr); 2064 if (pid_found) 2065 { 2066 if (process != NULL) 2067 { 2068 // Release our master pty file descriptor so the pty class doesn't 2069 // close it and so we can continue to use it in our STDIO thread 2070 int master_fd = pty.ReleaseMasterFD(); 2071 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2072 } 2073 DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); 2074 } 2075 else 2076 { 2077 DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); 2078 } 2079 return pid; 2080 } 2081 2082 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); 2083 return INVALID_NUB_PROCESS; 2084} 2085 2086#endif // #if defined (__arm__) 2087 2088 2089