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