brintos

brintos / llvm-project-archived public Read only

0
0
Text · 63.3 KiB · 4d5afcf Raw
1925 lines · cpp
1//===-- DNB.cpp -------------------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9//  Created by Greg Clayton on 3/23/07.10//11//===----------------------------------------------------------------------===//12 13#include "DNB.h"14#include <cinttypes>15#include <csignal>16#include <cstdio>17#include <cstdlib>18#include <libproc.h>19#include <map>20#include <mutex>21#include <sys/resource.h>22#include <sys/stat.h>23#include <sys/sysctl.h>24#include <sys/types.h>25#include <sys/wait.h>26#include <unistd.h>27#include <vector>28 29#if defined(__APPLE__)30#include <pthread.h>31#include <sched.h>32#endif33 34#define TRY_KQUEUE 135 36#ifdef TRY_KQUEUE37#include <sys/event.h>38#include <sys/time.h>39#ifdef NOTE_EXIT_DETAIL40#define USE_KQUEUE41#endif42#endif43 44#include "CFBundle.h"45#include "CFString.h"46#include "DNBDataRef.h"47#include "DNBLog.h"48#include "DNBThreadResumeActions.h"49#include "DNBTimer.h"50#include "MacOSX/Genealogy.h"51#include "MacOSX/MachProcess.h"52#include "MacOSX/MachTask.h"53#include "MacOSX/ThreadInfo.h"54#include "RNBRemote.h"55 56typedef std::shared_ptr<MachProcess> MachProcessSP;57typedef std::map<nub_process_t, MachProcessSP> ProcessMap;58typedef ProcessMap::iterator ProcessMapIter;59typedef ProcessMap::const_iterator ProcessMapConstIter;60 61static size_t62GetAllInfosMatchingName(const char *process_name,63                        std::vector<struct kinfo_proc> &matching_proc_infos);64 65// A Thread safe singleton to get a process map pointer.66//67// Returns a pointer to the existing process map, or a pointer to a68// newly created process map if CAN_CREATE is non-zero.69static ProcessMap *GetProcessMap(bool can_create) {70  static ProcessMap *g_process_map_ptr = NULL;71 72  if (can_create && g_process_map_ptr == NULL) {73    static std::mutex g_process_map_mutex;74    std::lock_guard<std::mutex> guard(g_process_map_mutex);75    if (g_process_map_ptr == NULL)76      g_process_map_ptr = new ProcessMap;77  }78  return g_process_map_ptr;79}80 81// Add PID to the shared process pointer map.82//83// Return non-zero value if we succeed in adding the process to the map.84// The only time this should fail is if we run out of memory and can't85// allocate a ProcessMap.86static nub_bool_t AddProcessToMap(nub_process_t pid, MachProcessSP &procSP) {87  ProcessMap *process_map = GetProcessMap(true);88  if (process_map) {89    process_map->insert(std::make_pair(pid, procSP));90    return true;91  }92  return false;93}94 95// Remove the shared pointer for PID from the process map.96//97// Returns the number of items removed from the process map.98// static size_t99// RemoveProcessFromMap (nub_process_t pid)100//{101//    ProcessMap* process_map = GetProcessMap(false);102//    if (process_map)103//    {104//        return process_map->erase(pid);105//    }106//    return 0;107//}108 109// Get the shared pointer for PID from the existing process map.110//111// Returns true if we successfully find a shared pointer to a112// MachProcess object.113static nub_bool_t GetProcessSP(nub_process_t pid, MachProcessSP &procSP) {114  ProcessMap *process_map = GetProcessMap(false);115  if (process_map != NULL) {116    ProcessMapIter pos = process_map->find(pid);117    if (pos != process_map->end()) {118      procSP = pos->second;119      return true;120    }121  }122  procSP.reset();123  return false;124}125 126#ifdef USE_KQUEUE127void *kqueue_thread(void *arg) {128  int kq_id = (int)(intptr_t)arg;129 130#if defined(__APPLE__)131  pthread_setname_np("kqueue thread");132#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)133  struct sched_param thread_param;134  int thread_sched_policy;135  if (pthread_getschedparam(pthread_self(), &thread_sched_policy,136                            &thread_param) == 0) {137    thread_param.sched_priority = 47;138    pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);139  }140#endif141#endif142 143  struct kevent death_event;144  while (true) {145    int n_events = kevent(kq_id, NULL, 0, &death_event, 1, NULL);146    if (n_events == -1) {147      if (errno == EINTR)148        continue;149      else {150        DNBLogError("kqueue failed with error: (%d): %s", errno,151                    strerror(errno));152        return NULL;153      }154    } else if (death_event.flags & EV_ERROR) {155      int error_no = static_cast<int>(death_event.data);156      const char *error_str = strerror(error_no);157      if (error_str == NULL)158        error_str = "Unknown error";159      DNBLogError("Failed to initialize kqueue event: (%d): %s", error_no,160                  error_str);161      return NULL;162    } else {163      int status;164      const pid_t pid = (pid_t)death_event.ident;165      const pid_t child_pid = waitpid(pid, &status, 0);166 167      bool exited = false;168      int signal = 0;169      int exit_status = 0;170      if (WIFSTOPPED(status)) {171        signal = WSTOPSIG(status);172        DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> STOPPED (signal = %i)",173                         child_pid, signal);174      } else if (WIFEXITED(status)) {175        exit_status = WEXITSTATUS(status);176        exited = true;177        DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> EXITED (status = %i)",178                         child_pid, exit_status);179      } else if (WIFSIGNALED(status)) {180        signal = WTERMSIG(status);181        if (child_pid == abs(pid)) {182          DNBLogThreadedIf(LOG_PROCESS,183                           "waitpid (%i) -> SIGNALED and EXITED (signal = %i)",184                           child_pid, signal);185          char exit_info[64];186          ::snprintf(exit_info, sizeof(exit_info),187                     "Terminated due to signal %i", signal);188          DNBProcessSetExitInfo(child_pid, exit_info);189          exited = true;190          exit_status = INT8_MAX;191        } else {192          DNBLogThreadedIf(LOG_PROCESS,193                           "waitpid (%i) -> SIGNALED (signal = %i)", child_pid,194                           signal);195        }196      }197 198      if (exited) {199        if (death_event.data & NOTE_EXIT_MEMORY)200          DNBProcessSetExitInfo(child_pid, "Terminated due to memory issue");201        else if (death_event.data & NOTE_EXIT_DECRYPTFAIL)202          DNBProcessSetExitInfo(child_pid, "Terminated due to decrypt failure");203        else if (death_event.data & NOTE_EXIT_CSERROR)204          DNBProcessSetExitInfo(child_pid,205                                "Terminated due to code signing error");206 207        DNBLogThreadedIf(208            LOG_PROCESS,209            "waitpid_process_thread (): setting exit status for pid = %i to %i",210            child_pid, exit_status);211        DNBProcessSetExitStatus(child_pid, status);212        return NULL;213      }214    }215  }216}217 218static bool spawn_kqueue_thread(pid_t pid) {219  pthread_t thread;220  int kq_id;221 222  kq_id = kqueue();223  if (kq_id == -1) {224    DNBLogError("Could not get kqueue for pid = %i.", pid);225    return false;226  }227 228  struct kevent reg_event;229 230  EV_SET(&reg_event, pid, EVFILT_PROC, EV_ADD,231         NOTE_EXIT | NOTE_EXITSTATUS | NOTE_EXIT_DETAIL, 0, NULL);232  // Register the event:233  int result = kevent(kq_id, &reg_event, 1, NULL, 0, NULL);234  if (result != 0) {235    DNBLogError(236        "Failed to register kqueue NOTE_EXIT event for pid %i, error: %d.", pid,237        result);238    return false;239  }240 241  int ret =242      ::pthread_create(&thread, NULL, kqueue_thread, (void *)(intptr_t)kq_id);243 244  // pthread_create returns 0 if successful245  if (ret == 0) {246    ::pthread_detach(thread);247    return true;248  }249  return false;250}251#endif // #if USE_KQUEUE252 253static void *waitpid_thread(void *arg) {254  const pid_t pid = (pid_t)(intptr_t)arg;255  int status;256 257#if defined(__APPLE__)258  pthread_setname_np("waitpid thread");259#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)260  struct sched_param thread_param;261  int thread_sched_policy;262  if (pthread_getschedparam(pthread_self(), &thread_sched_policy,263                            &thread_param) == 0) {264    thread_param.sched_priority = 47;265    pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);266  }267#endif268#endif269 270  while (true) {271    pid_t child_pid = waitpid(pid, &status, 0);272    DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): waitpid (pid = %i, "273                                  "&status, 0) => %i, status = %i, errno = %i",274                     pid, child_pid, status, errno);275 276    if (child_pid < 0) {277      if (errno == EINTR)278        continue;279      break;280    } else {281      if (WIFSTOPPED(status)) {282        continue;283      } else // if (WIFEXITED(status) || WIFSIGNALED(status))284      {285        DNBLogThreadedIf(286            LOG_PROCESS,287            "waitpid_thread (): setting exit status for pid = %i to %i",288            child_pid, status);289        DNBProcessSetExitStatus(child_pid, status);290        return NULL;291      }292    }293  }294 295  // We should never exit as long as our child process is alive, so if we296  // do something else went wrong and we should exit...297  DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): main loop exited, setting "298                                "exit status to an invalid value (-1) for pid "299                                "%i",300                   pid);301  DNBProcessSetExitStatus(pid, -1);302  return NULL;303}304static bool spawn_waitpid_thread(pid_t pid) {305#ifdef USE_KQUEUE306  bool success = spawn_kqueue_thread(pid);307  if (success)308    return true;309#endif310 311  pthread_t thread;312  int ret =313      ::pthread_create(&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);314  // pthread_create returns 0 if successful315  if (ret == 0) {316    ::pthread_detach(thread);317    return true;318  }319  return false;320}321 322nub_process_t DNBProcessLaunch(323    RNBContext *ctx, const char *path, char const *argv[], const char *envp[],324    const char *working_directory, // NULL => don't change, non-NULL => set325                                   // working directory for inferior to this326    const char *stdin_path, const char *stdout_path, const char *stderr_path,327    bool no_stdio, int disable_aslr, const char *event_data, char *err_str,328    size_t err_len) {329  DNBLogThreadedIf(LOG_PROCESS,330                   "%s ( path='%s', argv = %p, envp = %p, "331                   "working_dir=%s, stdin=%s, stdout=%s, "332                   "stderr=%s, no-stdio=%i, launch_flavor = %u, "333                   "disable_aslr = %d, err = %p, err_len = "334                   "%llu) called...",335                   __FUNCTION__, path, static_cast<void *>(argv),336                   static_cast<void *>(envp), working_directory, stdin_path,337                   stdout_path, stderr_path, no_stdio, ctx->LaunchFlavor(),338                   disable_aslr, static_cast<void *>(err_str),339                   static_cast<uint64_t>(err_len));340 341  if (err_str && err_len > 0)342    err_str[0] = '\0';343  struct stat path_stat;344  if (::stat(path, &path_stat) == -1) {345    char stat_error[256];346    ::strerror_r(errno, stat_error, sizeof(stat_error));347    snprintf(err_str, err_len, "%s (%s)", stat_error, path);348    return INVALID_NUB_PROCESS;349  }350 351  MachProcessSP processSP(new MachProcess);352  if (processSP.get()) {353    DNBError launch_err;354    pid_t pid = processSP->LaunchForDebug(355        path, argv, envp, working_directory, stdin_path, stdout_path,356        stderr_path, no_stdio, ctx->LaunchFlavor(), disable_aslr, event_data,357        ctx->GetIgnoredExceptions(), launch_err);358    if (err_str) {359      *err_str = '\0';360      if (launch_err.Fail()) {361        const char *launch_err_str = launch_err.AsString();362        if (launch_err_str) {363          strlcpy(err_str, launch_err_str, err_len - 1);364          err_str[err_len - 1] =365              '\0'; // Make sure the error string is terminated366        }367      }368    }369 370    DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid);371 372    if (pid != INVALID_NUB_PROCESS) {373      // Spawn a thread to reap our child inferior process...374      spawn_waitpid_thread(pid);375 376      if (processSP->Task().TaskPortForProcessID(launch_err) == TASK_NULL) {377        // We failed to get the task for our process ID which is bad.378        // Kill our process otherwise it will be stopped at the entry379        // point and get reparented to someone else and never go away.380        DNBLog("Could not get task port for process, sending SIGKILL and "381               "exiting.");382        kill(SIGKILL, pid);383 384        if (err_str && err_len > 0) {385          if (launch_err.AsString()) {386            ::snprintf(err_str, err_len,387                       "failed to get the task for process %i: %s", pid,388                       launch_err.AsString());389          } else {390 391            const char *ent_name =392#if TARGET_OS_OSX393              "com.apple.security.get-task-allow";394#else395              "get-task-allow";396#endif397            ::snprintf(err_str, err_len,398                       "failed to get the task for process %i: this likely "399                       "means the process cannot be debugged, either because "400                       "it's a system process or because the process is "401                       "missing the %s entitlement.",402                       pid, ent_name);403          }404        }405      } else {406        bool res = AddProcessToMap(pid, processSP);407        UNUSED_IF_ASSERT_DISABLED(res);408        assert(res && "Couldn't add process to map!");409        return pid;410      }411    }412  }413  return INVALID_NUB_PROCESS;414}415 416// If there is one process with a given name, return the pid for that process.417nub_process_t DNBProcessGetPIDByName(const char *name) {418  std::vector<struct kinfo_proc> matching_proc_infos;419  size_t num_matching_proc_infos =420      GetAllInfosMatchingName(name, matching_proc_infos);421  if (num_matching_proc_infos == 1) {422    return matching_proc_infos[0].kp_proc.p_pid;423  }424  return INVALID_NUB_PROCESS;425}426 427nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout,428                                     const RNBContext::IgnoredExceptions 429                                             &ignored_exceptions, char *err_str,430                                     size_t err_len) {431  if (err_str && err_len > 0)432    err_str[0] = '\0';433  std::vector<struct kinfo_proc> matching_proc_infos;434  size_t num_matching_proc_infos =435      GetAllInfosMatchingName(name, matching_proc_infos);436  if (num_matching_proc_infos == 0) {437    DNBLogError("error: no processes match '%s'\n", name);438    return INVALID_NUB_PROCESS;439  }440  if (num_matching_proc_infos > 1) {441    DNBLogError("error: %llu processes match '%s':\n",442                (uint64_t)num_matching_proc_infos, name);443    size_t i;444    for (i = 0; i < num_matching_proc_infos; ++i)445      DNBLogError("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid,446                  matching_proc_infos[i].kp_proc.p_comm);447    return INVALID_NUB_PROCESS;448  }449 450  return DNBProcessAttach(matching_proc_infos[0].kp_proc.p_pid, timeout,451                          ignored_exceptions, err_str, err_len);452}453 454nub_process_t DNBProcessAttach(nub_process_t attach_pid,455                               struct timespec *timeout, 456                               const RNBContext::IgnoredExceptions 457                                       &ignored_exceptions,458                               char *err_str, size_t err_len) {459  if (err_str && err_len > 0)460    err_str[0] = '\0';461 462  if (getenv("LLDB_DEBUGSERVER_PATH") == NULL) {463    int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID,464                 static_cast<int>(attach_pid)};465    struct kinfo_proc processInfo;466    size_t bufsize = sizeof(processInfo);467    if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo,468               &bufsize, NULL, 0) == 0 &&469        bufsize > 0) {470 471      if ((processInfo.kp_proc.p_flag & P_TRANSLATED) == P_TRANSLATED) {472        const char *translated_debugserver =473            "/Library/Apple/usr/libexec/oah/debugserver";474        char fdstr[16];475        char pidstr[16];476        extern int communication_fd;477 478        if (communication_fd == -1) {479          DNBLogError("Trying to attach to a translated process with the "480                      "native debugserver, exiting...\n");481          return INVALID_NUB_PROCESS_ARCH;482        }483 484        struct stat st;485        if (::stat(translated_debugserver, &st) != 0) {486          DNBLogError("Translated inferior process but Rosetta debugserver not "487                      "found at %s",488                      translated_debugserver);489          return INVALID_NUB_PROCESS_ARCH;490        }491 492        snprintf(fdstr, sizeof(fdstr), "--fd=%d", communication_fd);493        snprintf(pidstr, sizeof(pidstr), "--attach=%d", attach_pid);494        execl(translated_debugserver, translated_debugserver, "--native-regs",495              "--setsid", fdstr, "--handoff-attach-from-native", pidstr,496              (char *)0);497        DNBLogThreadedIf(LOG_PROCESS, "Failed to launch debugserver for "498                         "translated process: ", errno, strerror(errno));499        __builtin_trap();500      }501    }502  }503 504  if (DNBDebugserverIsTranslated()) {505    return INVALID_NUB_PROCESS_ARCH;506  }507 508  pid_t pid = INVALID_NUB_PROCESS;509  MachProcessSP processSP(new MachProcess);510  if (processSP.get()) {511    DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...",512                     attach_pid);513    pid =514        processSP->AttachForDebug(attach_pid, ignored_exceptions, err_str, 515                                  err_len);516 517    if (pid != INVALID_NUB_PROCESS) {518      bool res = AddProcessToMap(pid, processSP);519      UNUSED_IF_ASSERT_DISABLED(res);520      assert(res && "Couldn't add process to map!");521      spawn_waitpid_thread(pid);522    }523  }524 525  while (pid != INVALID_NUB_PROCESS) {526    // Wait for process to start up and hit entry point527    DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "528                                  "eEventProcessRunningStateChanged | "529                                  "eEventProcessStoppedStateChanged, true, "530                                  "INFINITE)...",531                     __FUNCTION__, pid);532    nub_event_t set_events =533        DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |534                                         eEventProcessStoppedStateChanged,535                                true, timeout);536 537    DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "538                                  "eEventProcessRunningStateChanged | "539                                  "eEventProcessStoppedStateChanged, true, "540                                  "INFINITE) => 0x%8.8x",541                     __FUNCTION__, pid, set_events);542 543    if (set_events == 0) {544      if (err_str && err_len > 0)545        snprintf(err_str, err_len,546                 "attached to process, but could not pause execution; attach "547                 "failed");548      pid = INVALID_NUB_PROCESS;549    } else {550      if (set_events & (eEventProcessRunningStateChanged |551                        eEventProcessStoppedStateChanged)) {552        nub_state_t pid_state = DNBProcessGetState(pid);553        DNBLogThreadedIf(554            LOG_PROCESS,555            "%s process %4.4x state changed (eEventProcessStateChanged): %s",556            __FUNCTION__, pid, DNBStateAsString(pid_state));557 558        switch (pid_state) {559        case eStateInvalid:560        case eStateUnloaded:561        case eStateAttaching:562        case eStateLaunching:563        case eStateSuspended:564          break; // Ignore565 566        case eStateRunning:567        case eStateStepping:568          // Still waiting to stop at entry point...569          break;570 571        case eStateStopped:572        case eStateCrashed:573          return pid;574 575        case eStateDetached:576        case eStateExited:577          if (err_str && err_len > 0)578            snprintf(err_str, err_len, "process exited");579          return INVALID_NUB_PROCESS;580        }581      }582 583      DNBProcessResetEvents(pid, set_events);584    }585  }586 587  return INVALID_NUB_PROCESS;588}589 590size_t DNBGetAllInfos(std::vector<struct kinfo_proc> &proc_infos) {591  size_t size = 0;592  int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};593  u_int namelen = sizeof(name) / sizeof(int);594  int err;595 596  // Try to find out how many processes are around so we can597  // size the buffer appropriately.  sysctl's man page specifically suggests598  // this approach, and says it returns a bit larger size than needed to599  // handle any new processes created between then and now.600 601  err = ::sysctl(name, namelen, NULL, &size, NULL, 0);602 603  if ((err < 0) && (err != ENOMEM)) {604    proc_infos.clear();605    perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");606    return 0;607  }608 609  // Increase the size of the buffer by a few processes in case more have610  // been spawned611  proc_infos.resize(size / sizeof(struct kinfo_proc));612  size = proc_infos.size() *613         sizeof(struct kinfo_proc); // Make sure we don't exceed our resize...614  err = ::sysctl(name, namelen, &proc_infos[0], &size, NULL, 0);615  if (err < 0) {616    proc_infos.clear();617    return 0;618  }619 620  // Trim down our array to fit what we actually got back621  proc_infos.resize(size / sizeof(struct kinfo_proc));622  return proc_infos.size();623}624 625JSONGenerator::ObjectSP DNBGetDyldProcessState(nub_process_t pid) {626  MachProcessSP procSP;627  if (GetProcessSP(pid, procSP)) {628    return procSP->GetDyldProcessState();629  }630  return {};631}632 633static size_t634GetAllInfosMatchingName(const char *full_process_name,635                        std::vector<struct kinfo_proc> &matching_proc_infos) {636 637  matching_proc_infos.clear();638  if (full_process_name && full_process_name[0]) {639    // We only get the process name, not the full path, from the proc_info.  So640    // just take the641    // base name of the process name...642    const char *process_name;643    process_name = strrchr(full_process_name, '/');644    if (process_name == NULL)645      process_name = full_process_name;646    else647      process_name++;648 649    const size_t process_name_len = strlen(process_name);650    std::vector<struct kinfo_proc> proc_infos;651    const size_t num_proc_infos = DNBGetAllInfos(proc_infos);652    if (num_proc_infos > 0) {653      uint32_t i;654      for (i = 0; i < num_proc_infos; i++) {655        // Skip zombie processes and processes with unset status656        if (proc_infos[i].kp_proc.p_stat == 0 ||657            proc_infos[i].kp_proc.p_stat == SZOMB)658          continue;659 660        // Check for process by name. We only check the first MAXCOMLEN661        // chars as that is all that kp_proc.p_comm holds.662 663        if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm,664                          MAXCOMLEN) == 0) {665          if (process_name_len > MAXCOMLEN) {666            // We found a matching process name whose first MAXCOMLEN667            // characters match, but there is more to the name than668            // this. We need to get the full process name.  Use proc_pidpath,669            // which will get670            // us the full path to the executed process.671 672            char proc_path_buf[PATH_MAX];673 674            int return_val = proc_pidpath(proc_infos[i].kp_proc.p_pid,675                                          proc_path_buf, PATH_MAX);676            if (return_val > 0) {677              // Okay, now search backwards from that to see if there is a678              // slash in the name.  Note, even though we got all the args we679              // don't care680              // because the list data is just a bunch of concatenated null681              // terminated strings682              // so strrchr will start from the end of argv0.683 684              const char *argv_basename = strrchr(proc_path_buf, '/');685              if (argv_basename) {686                // Skip the '/'687                ++argv_basename;688              } else {689                // We didn't find a directory delimiter in the process argv[0],690                // just use what was in there691                argv_basename = proc_path_buf;692              }693 694              if (argv_basename) {695                if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0) {696                  matching_proc_infos.push_back(proc_infos[i]);697                }698              }699            }700          } else {701            // We found a matching process, add it to our list702            matching_proc_infos.push_back(proc_infos[i]);703          }704        }705      }706    }707  }708  // return the newly added matches.709  return matching_proc_infos.size();710}711 712nub_process_t713DNBProcessAttachWait(RNBContext *ctx, const char *waitfor_process_name,714                     bool ignore_existing, struct timespec *timeout_abstime,715                     useconds_t waitfor_interval, char *err_str, size_t err_len,716                     DNBShouldCancelCallback should_cancel_callback,717                     void *callback_data) {718  DNBError prepare_error;719  std::vector<struct kinfo_proc> exclude_proc_infos;720  size_t num_exclude_proc_infos;721 722  nub_launch_flavor_t launch_flavor = ctx->LaunchFlavor();723 724  // If the PrepareForAttach returns a valid token, use  MachProcess to check725  // for the process, otherwise scan the process table.726 727  const void *attach_token = MachProcess::PrepareForAttach(728      waitfor_process_name, launch_flavor, true, prepare_error);729 730  if (prepare_error.Fail()) {731    DNBLogError("Error in PrepareForAttach: %s", prepare_error.AsString());732    return INVALID_NUB_PROCESS;733  }734 735  if (attach_token == NULL) {736    if (ignore_existing)737      num_exclude_proc_infos =738          GetAllInfosMatchingName(waitfor_process_name, exclude_proc_infos);739    else740      num_exclude_proc_infos = 0;741  }742 743  DNBLogThreadedIf(LOG_PROCESS, "Waiting for '%s' to appear...\n",744                   waitfor_process_name);745 746  // Loop and try to find the process by name747  nub_process_t waitfor_pid = INVALID_NUB_PROCESS;748 749  while (waitfor_pid == INVALID_NUB_PROCESS) {750    if (attach_token != NULL) {751      nub_process_t pid;752      pid = MachProcess::CheckForProcess(attach_token, launch_flavor);753      if (pid != INVALID_NUB_PROCESS) {754        waitfor_pid = pid;755        break;756      }757    } else {758      // Get the current process list, and check for matches that759      // aren't in our original list. If anyone wants to attach760      // to an existing process by name, they should do it with761      // --attach=PROCNAME. Else we will wait for the first matching762      // process that wasn't in our exclusion list.763      std::vector<struct kinfo_proc> proc_infos;764      const size_t num_proc_infos =765          GetAllInfosMatchingName(waitfor_process_name, proc_infos);766      for (size_t i = 0; i < num_proc_infos; i++) {767        nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;768        for (size_t j = 0; j < num_exclude_proc_infos; j++) {769          if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid) {770            // This process was in our exclusion list, don't use it.771            curr_pid = INVALID_NUB_PROCESS;772            break;773          }774        }775 776        // If we didn't find CURR_PID in our exclusion list, then use it.777        if (curr_pid != INVALID_NUB_PROCESS) {778          // We found our process!779          waitfor_pid = curr_pid;780          break;781        }782      }783    }784 785    // If we haven't found our process yet, check for a timeout786    // and then sleep for a bit until we poll again.787    if (waitfor_pid == INVALID_NUB_PROCESS) {788      if (timeout_abstime != NULL) {789        // Check to see if we have a waitfor-duration option that790        // has timed out?791        if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime)) {792          if (err_str && err_len > 0)793            snprintf(err_str, err_len, "operation timed out");794          DNBLogError("error: waiting for process '%s' timed out.\n",795                      waitfor_process_name);796          return INVALID_NUB_PROCESS;797        }798      }799 800      // Call the should cancel callback as well...801 802      if (should_cancel_callback != NULL &&803          should_cancel_callback(callback_data)) {804        DNBLogThreadedIf(805            LOG_PROCESS,806            "DNBProcessAttachWait cancelled by should_cancel callback.");807        waitfor_pid = INVALID_NUB_PROCESS;808        break;809      }810 811      // Now we're going to wait a while before polling again.  But we also812      // need to check whether we've gotten an event from the debugger  813      // telling us to interrupt the wait.  So we'll use the wait for a possible814      // next event to also be our short pause...815      struct timespec short_timeout;816      DNBTimer::OffsetTimeOfDay(&short_timeout, 0, waitfor_interval);817      uint32_t event_mask = RNBContext::event_read_packet_available 818          | RNBContext::event_read_thread_exiting;819      nub_event_t set_events = ctx->Events().WaitForSetEvents(event_mask, 820          &short_timeout);821      if (set_events & RNBContext::event_read_packet_available) {822        // If we get any packet from the debugger while waiting on the async,823        // it has to be telling us to interrupt.  So always exit here.824        // Over here in DNB land we can see that there was a packet, but all825        // the methods to actually handle it are protected.  It's not worth826        // rearranging all that just to get which packet we were sent...827        DNBLogError("Interrupted by packet while waiting for '%s' to appear.\n",828                   waitfor_process_name);829        break;830      }831      if (set_events & RNBContext::event_read_thread_exiting) {832        // The packet thread is shutting down, get out of here...833        DNBLogError("Interrupted by packet thread shutdown while waiting for "834                    "%s to appear.\n", waitfor_process_name);835        break;836      }837      838    }839  }840 841  if (waitfor_pid != INVALID_NUB_PROCESS) {842    DNBLogThreadedIf(LOG_PROCESS, "Attaching to %s with pid %i...\n",843                     waitfor_process_name, waitfor_pid);844    // In some cases, we attempt to attach during the transition from845    // /usr/lib/dyld to the dyld in the shared cache. If that happens, we may846    // end up in a state where there is no dyld in the process and from there847    // the debugging session is doomed.848    // In an attempt to make this scenario much less likely, we sleep849    // for an additional `waitfor_interval` number of microseconds before850    // attaching.851    ::usleep(waitfor_interval);852    waitfor_pid = DNBProcessAttach(waitfor_pid, timeout_abstime,853                                   ctx->GetIgnoredExceptions(), err_str, 854                                   err_len);855  }856 857  bool success = waitfor_pid != INVALID_NUB_PROCESS;858  MachProcess::CleanupAfterAttach(attach_token, launch_flavor, success,859                                  prepare_error);860 861  return waitfor_pid;862}863 864nub_bool_t DNBProcessDetach(nub_process_t pid) {865  MachProcessSP procSP;866  if (GetProcessSP(pid, procSP)) {867    const bool remove = true;868    DNBLogThreaded(869        "Disabling breakpoints and watchpoints, and detaching from %d.", pid);870    procSP->DisableAllBreakpoints(remove);871    procSP->DisableAllWatchpoints(remove);872    return procSP->Detach();873  }874  return false;875}876 877nub_bool_t DNBProcessKill(nub_process_t pid) {878  MachProcessSP procSP;879  if (GetProcessSP(pid, procSP)) {880    return procSP->Kill();881  }882  return false;883}884 885nub_bool_t DNBProcessSignal(nub_process_t pid, int signal) {886  MachProcessSP procSP;887  if (GetProcessSP(pid, procSP)) {888    return procSP->Signal(signal);889  }890  return false;891}892 893nub_bool_t DNBProcessInterrupt(nub_process_t pid) {894  MachProcessSP procSP;895  if (GetProcessSP(pid, procSP))896    return procSP->Interrupt();897  return false;898}899 900nub_bool_t DNBProcessSendEvent(nub_process_t pid, const char *event) {901  MachProcessSP procSP;902  if (GetProcessSP(pid, procSP)) {903    // FIXME: Do something with the error...904    DNBError send_error;905    return procSP->SendEvent(event, send_error);906  }907  return false;908}909 910nub_bool_t DNBProcessIsAlive(nub_process_t pid) {911  MachProcessSP procSP;912  if (GetProcessSP(pid, procSP)) {913    return MachTask::IsValid(procSP->Task().TaskPort());914  }915  return eStateInvalid;916}917 918// Process and Thread state information919nub_state_t DNBProcessGetState(nub_process_t pid) {920  MachProcessSP procSP;921  if (GetProcessSP(pid, procSP)) {922    return procSP->GetState();923  }924  return eStateInvalid;925}926 927// Process and Thread state information928nub_bool_t DNBProcessGetExitStatus(nub_process_t pid, int *status) {929  MachProcessSP procSP;930  if (GetProcessSP(pid, procSP)) {931    return procSP->GetExitStatus(status);932  }933  return false;934}935 936nub_bool_t DNBProcessSetExitStatus(nub_process_t pid, int status) {937  MachProcessSP procSP;938  if (GetProcessSP(pid, procSP)) {939    procSP->SetExitStatus(status);940    return true;941  }942  return false;943}944 945const char *DNBProcessGetExitInfo(nub_process_t pid) {946  MachProcessSP procSP;947  if (GetProcessSP(pid, procSP)) {948    return procSP->GetExitInfo();949  }950  return NULL;951}952 953nub_bool_t DNBProcessSetExitInfo(nub_process_t pid, const char *info) {954  MachProcessSP procSP;955  if (GetProcessSP(pid, procSP)) {956    procSP->SetExitInfo(info);957    return true;958  }959  return false;960}961 962const char *DNBThreadGetName(nub_process_t pid, nub_thread_t tid) {963  MachProcessSP procSP;964  if (GetProcessSP(pid, procSP))965    return procSP->ThreadGetName(tid);966  return NULL;967}968 969nub_bool_t970DNBThreadGetIdentifierInfo(nub_process_t pid, nub_thread_t tid,971                           thread_identifier_info_data_t *ident_info) {972  MachProcessSP procSP;973  if (GetProcessSP(pid, procSP))974    return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);975  return false;976}977 978nub_state_t DNBThreadGetState(nub_process_t pid, nub_thread_t tid) {979  MachProcessSP procSP;980  if (GetProcessSP(pid, procSP)) {981    return procSP->ThreadGetState(tid);982  }983  return eStateInvalid;984}985 986const char *DNBStateAsString(nub_state_t state) {987  switch (state) {988  case eStateInvalid:989    return "Invalid";990  case eStateUnloaded:991    return "Unloaded";992  case eStateAttaching:993    return "Attaching";994  case eStateLaunching:995    return "Launching";996  case eStateStopped:997    return "Stopped";998  case eStateRunning:999    return "Running";1000  case eStateStepping:1001    return "Stepping";1002  case eStateCrashed:1003    return "Crashed";1004  case eStateDetached:1005    return "Detached";1006  case eStateExited:1007    return "Exited";1008  case eStateSuspended:1009    return "Suspended";1010  }1011  return "nub_state_t ???";1012}1013 1014Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread(nub_process_t pid,1015                                                         nub_thread_t tid,1016                                                         bool &timed_out) {1017  Genealogy::ThreadActivitySP thread_activity_sp;1018  MachProcessSP procSP;1019  if (GetProcessSP(pid, procSP))1020    thread_activity_sp = procSP->GetGenealogyInfoForThread(tid, timed_out);1021  return thread_activity_sp;1022}1023 1024Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo(nub_process_t pid,1025                                                            size_t idx) {1026  Genealogy::ProcessExecutableInfoSP image_info_sp;1027  MachProcessSP procSP;1028  if (GetProcessSP(pid, procSP)) {1029    image_info_sp = procSP->GetGenealogyImageInfo(idx);1030  }1031  return image_info_sp;1032}1033 1034ThreadInfo::QoS DNBGetRequestedQoSForThread(nub_process_t pid, nub_thread_t tid,1035                                            nub_addr_t tsd,1036                                            uint64_t dti_qos_class_index) {1037  MachProcessSP procSP;1038  if (GetProcessSP(pid, procSP)) {1039    return procSP->GetRequestedQoS(tid, tsd, dti_qos_class_index);1040  }1041  return ThreadInfo::QoS();1042}1043 1044nub_addr_t DNBGetPThreadT(nub_process_t pid, nub_thread_t tid) {1045  MachProcessSP procSP;1046  if (GetProcessSP(pid, procSP)) {1047    return procSP->GetPThreadT(tid);1048  }1049  return INVALID_NUB_ADDRESS;1050}1051 1052nub_addr_t DNBGetDispatchQueueT(nub_process_t pid, nub_thread_t tid) {1053  MachProcessSP procSP;1054  if (GetProcessSP(pid, procSP)) {1055    return procSP->GetDispatchQueueT(tid);1056  }1057  return INVALID_NUB_ADDRESS;1058}1059 1060nub_addr_t1061DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,1062                          uint64_t plo_pthread_tsd_base_address_offset,1063                          uint64_t plo_pthread_tsd_base_offset,1064                          uint64_t plo_pthread_tsd_entry_size) {1065  MachProcessSP procSP;1066  if (GetProcessSP(pid, procSP)) {1067    return procSP->GetTSDAddressForThread(1068        tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,1069        plo_pthread_tsd_entry_size);1070  }1071  return INVALID_NUB_ADDRESS;1072}1073 1074std::optional<std::pair<cpu_type_t, cpu_subtype_t>>1075DNBGetMainBinaryCPUTypes(nub_process_t pid) {1076  MachProcessSP procSP;1077  if (GetProcessSP(pid, procSP))1078    return procSP->GetMainBinaryCPUTypes(pid);1079  return {};1080}1081 1082JSONGenerator::ObjectSP1083DNBGetAllLoadedLibrariesInfos(nub_process_t pid, bool report_load_commands) {1084  MachProcessSP procSP;1085  if (GetProcessSP(pid, procSP)) {1086    return procSP->GetAllLoadedLibrariesInfos(pid, report_load_commands);1087  }1088  return JSONGenerator::ObjectSP();1089}1090 1091JSONGenerator::ObjectSP1092DNBGetLibrariesInfoForAddresses(nub_process_t pid,1093                                std::vector<uint64_t> &macho_addresses) {1094  MachProcessSP procSP;1095  if (GetProcessSP(pid, procSP)) {1096    return procSP->GetLibrariesInfoForAddresses(pid, macho_addresses);1097  }1098  return JSONGenerator::ObjectSP();1099}1100 1101JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid) {1102  MachProcessSP procSP;1103  if (GetProcessSP(pid, procSP)) {1104    return procSP->GetInferiorSharedCacheInfo(pid);1105  }1106  return JSONGenerator::ObjectSP();1107}1108 1109const char *DNBProcessGetExecutablePath(nub_process_t pid) {1110  MachProcessSP procSP;1111  if (GetProcessSP(pid, procSP)) {1112    return procSP->Path();1113  }1114  return NULL;1115}1116 1117nub_size_t DNBProcessGetArgumentCount(nub_process_t pid) {1118  MachProcessSP procSP;1119  if (GetProcessSP(pid, procSP)) {1120    return procSP->ArgumentCount();1121  }1122  return 0;1123}1124 1125const char *DNBProcessGetArgumentAtIndex(nub_process_t pid, nub_size_t idx) {1126  MachProcessSP procSP;1127  if (GetProcessSP(pid, procSP)) {1128    return procSP->ArgumentAtIndex(idx);1129  }1130  return NULL;1131}1132 1133// Execution control1134nub_bool_t DNBProcessResume(nub_process_t pid,1135                            const DNBThreadResumeAction *actions,1136                            size_t num_actions) {1137  DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);1138  MachProcessSP procSP;1139  if (GetProcessSP(pid, procSP)) {1140    DNBThreadResumeActions thread_actions(actions, num_actions);1141 1142    // Below we add a default thread plan just in case one wasn't1143    // provided so all threads always know what they were supposed to do1144    if (thread_actions.IsEmpty()) {1145      // No thread plans were given, so the default it to run all threads1146      thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);1147    } else {1148      // Some thread plans were given which means anything that wasn't1149      // specified should remain stopped.1150      thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);1151    }1152    return procSP->Resume(thread_actions);1153  }1154  return false;1155}1156 1157nub_bool_t DNBProcessHalt(nub_process_t pid) {1158  DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);1159  MachProcessSP procSP;1160  if (GetProcessSP(pid, procSP))1161    return procSP->Signal(SIGSTOP);1162  return false;1163}1164//1165// nub_bool_t1166// DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)1167//{1168//    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)",1169//    __FUNCTION__, pid, tid, (uint32_t)step);1170//    MachProcessSP procSP;1171//    if (GetProcessSP (pid, procSP))1172//    {1173//        return procSP->Resume(tid, step, 0);1174//    }1175//    return false;1176//}1177//1178// nub_bool_t1179// DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t1180// step, int signal)1181//{1182//    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u,1183//    signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);1184//    MachProcessSP procSP;1185//    if (GetProcessSP (pid, procSP))1186//    {1187//        return procSP->Resume(tid, step, signal);1188//    }1189//    return false;1190//}1191 1192nub_event_t DNBProcessWaitForEvents(nub_process_t pid, nub_event_t event_mask,1193                                    bool wait_for_set,1194                                    struct timespec *timeout) {1195  nub_event_t result = 0;1196  MachProcessSP procSP;1197  if (GetProcessSP(pid, procSP)) {1198    if (wait_for_set)1199      result = procSP->Events().WaitForSetEvents(event_mask, timeout);1200    else1201      result = procSP->Events().WaitForEventsToReset(event_mask, timeout);1202  }1203  return result;1204}1205 1206void DNBProcessResetEvents(nub_process_t pid, nub_event_t event_mask) {1207  MachProcessSP procSP;1208  if (GetProcessSP(pid, procSP))1209    procSP->Events().ResetEvents(event_mask);1210}1211 1212// Breakpoints1213nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,1214                            nub_bool_t hardware) {1215  MachProcessSP procSP;1216  if (GetProcessSP(pid, procSP))1217    return procSP->CreateBreakpoint(addr, size, hardware) != NULL;1218  return false;1219}1220 1221nub_bool_t DNBBreakpointClear(nub_process_t pid, nub_addr_t addr) {1222  MachProcessSP procSP;1223  if (GetProcessSP(pid, procSP))1224    return procSP->DisableBreakpoint(addr, true);1225  return false; // Failed1226}1227 1228// Watchpoints1229nub_bool_t DNBWatchpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,1230                            uint32_t watch_flags, nub_bool_t hardware) {1231  MachProcessSP procSP;1232  if (GetProcessSP(pid, procSP))1233    return procSP->CreateWatchpoint(addr, size, watch_flags, hardware) != NULL;1234  return false;1235}1236 1237nub_bool_t DNBWatchpointClear(nub_process_t pid, nub_addr_t addr) {1238  MachProcessSP procSP;1239  if (GetProcessSP(pid, procSP))1240    return procSP->DisableWatchpoint(addr, true);1241  return false; // Failed1242}1243 1244// Return the number of supported hardware watchpoints.1245uint32_t DNBWatchpointGetNumSupportedHWP(nub_process_t pid) {1246  MachProcessSP procSP;1247  if (GetProcessSP(pid, procSP))1248    return procSP->GetNumSupportedHardwareWatchpoints();1249  return 0;1250}1251 1252// Read memory in the address space of process PID. This call will take1253// care of setting and restoring permissions and breaking up the memory1254// read into multiple chunks as required.1255//1256// RETURNS: number of bytes actually read1257nub_size_t DNBProcessMemoryRead(nub_process_t pid, nub_addr_t addr,1258                                nub_size_t size, void *buf) {1259  MachProcessSP procSP;1260  if (GetProcessSP(pid, procSP))1261    return procSP->ReadMemory(addr, size, buf);1262  return 0;1263}1264 1265uint64_t DNBProcessMemoryReadInteger(nub_process_t pid, nub_addr_t addr,1266                                     nub_size_t integer_size,1267                                     uint64_t fail_value) {1268  union Integers {1269    uint8_t u8;1270    uint16_t u16;1271    uint32_t u32;1272    uint64_t u64;1273  };1274 1275  if (integer_size <= sizeof(uint64_t)) {1276    Integers ints;1277    if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) {1278      switch (integer_size) {1279      case 1:1280        return ints.u8;1281      case 2:1282        return ints.u16;1283      case 3:1284        return ints.u32 & 0xffffffu;1285      case 4:1286        return ints.u32;1287      case 5:1288        return ints.u32 & 0x000000ffffffffffull;1289      case 6:1290        return ints.u32 & 0x0000ffffffffffffull;1291      case 7:1292        return ints.u32 & 0x00ffffffffffffffull;1293      case 8:1294        return ints.u64;1295      }1296    }1297  }1298  return fail_value;1299}1300 1301nub_addr_t DNBProcessMemoryReadPointer(nub_process_t pid, nub_addr_t addr) {1302  cpu_type_t cputype = DNBProcessGetCPUType(pid);1303  if (cputype) {1304    const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4;1305    return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0);1306  }1307  return 0;1308}1309 1310std::string DNBProcessMemoryReadCString(nub_process_t pid, nub_addr_t addr) {1311  std::string cstr;1312  char buffer[256];1313  const nub_size_t max_buffer_cstr_length = sizeof(buffer) - 1;1314  buffer[max_buffer_cstr_length] = '\0';1315  nub_size_t length = 0;1316  nub_addr_t curr_addr = addr;1317  do {1318    nub_size_t bytes_read =1319        DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer);1320    if (bytes_read == 0)1321      break;1322    length = strlen(buffer);1323    cstr.append(buffer, length);1324    curr_addr += length;1325  } while (length == max_buffer_cstr_length);1326  return cstr;1327}1328 1329std::string DNBProcessMemoryReadCStringFixed(nub_process_t pid, nub_addr_t addr,1330                                             nub_size_t fixed_length) {1331  std::string cstr;1332  char buffer[fixed_length + 1];1333  buffer[fixed_length] = '\0';1334  nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer);1335  if (bytes_read > 0)1336    cstr.assign(buffer);1337  return cstr;1338}1339 1340// Write memory to the address space of process PID. This call will take1341// care of setting and restoring permissions and breaking up the memory1342// write into multiple chunks as required.1343//1344// RETURNS: number of bytes actually written1345nub_size_t DNBProcessMemoryWrite(nub_process_t pid, nub_addr_t addr,1346                                 nub_size_t size, const void *buf) {1347  MachProcessSP procSP;1348  if (GetProcessSP(pid, procSP))1349    return procSP->WriteMemory(addr, size, buf);1350  return 0;1351}1352 1353nub_addr_t DNBProcessMemoryAllocate(nub_process_t pid, nub_size_t size,1354                                    uint32_t permissions) {1355  MachProcessSP procSP;1356  if (GetProcessSP(pid, procSP))1357    return procSP->Task().AllocateMemory(size, permissions);1358  return 0;1359}1360 1361nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid, nub_addr_t addr) {1362  MachProcessSP procSP;1363  if (GetProcessSP(pid, procSP))1364    return procSP->Task().DeallocateMemory(addr);1365  return 0;1366}1367 1368// Find attributes of the memory region that contains ADDR for process PID,1369// if possible, and return a string describing those attributes.1370//1371// Returns 1 if we could find attributes for this region and OUTBUF can1372// be sent to the remote debugger.1373//1374// Returns 0 if we couldn't find the attributes for a region of memory at1375// that address and OUTBUF should not be sent.1376//1377// Returns -1 if this platform cannot look up information about memory regions1378// or if we do not yet have a valid launched process.1379//1380int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,1381                               DNBRegionInfo *region_info) {1382  MachProcessSP procSP;1383  if (GetProcessSP(pid, procSP))1384    return procSP->Task().GetMemoryRegionInfo(addr, region_info);1385 1386  return -1;1387}1388 1389nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr,1390                                   nub_size_t size,1391                                   std::vector<uint8_t> &tags) {1392  MachProcessSP procSP;1393  if (GetProcessSP(pid, procSP))1394    return procSP->Task().GetMemoryTags(addr, size, tags);1395 1396  return false;1397}1398 1399std::string DNBProcessGetProfileData(nub_process_t pid,1400                                     DNBProfileDataScanType scanType) {1401  MachProcessSP procSP;1402  if (GetProcessSP(pid, procSP))1403    return procSP->Task().GetProfileData(scanType);1404 1405  return std::string("");1406}1407 1408nub_bool_t DNBProcessSetEnableAsyncProfiling(nub_process_t pid,1409                                             nub_bool_t enable,1410                                             uint64_t interval_usec,1411                                             DNBProfileDataScanType scan_type) {1412  MachProcessSP procSP;1413  if (GetProcessSP(pid, procSP)) {1414    procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);1415    return true;1416  }1417 1418  return false;1419}1420 1421// Get the number of threads for the specified process.1422nub_size_t DNBProcessGetNumThreads(nub_process_t pid) {1423  MachProcessSP procSP;1424  if (GetProcessSP(pid, procSP))1425    return procSP->GetNumThreads();1426  return 0;1427}1428 1429// Get the thread ID of the current thread.1430nub_thread_t DNBProcessGetCurrentThread(nub_process_t pid) {1431  MachProcessSP procSP;1432  if (GetProcessSP(pid, procSP))1433    return procSP->GetCurrentThread();1434  return 0;1435}1436 1437// Get the mach port number of the current thread.1438nub_thread_t DNBProcessGetCurrentThreadMachPort(nub_process_t pid) {1439  MachProcessSP procSP;1440  if (GetProcessSP(pid, procSP))1441    return procSP->GetCurrentThreadMachPort();1442  return 0;1443}1444 1445// Change the current thread.1446nub_thread_t DNBProcessSetCurrentThread(nub_process_t pid, nub_thread_t tid) {1447  MachProcessSP procSP;1448  if (GetProcessSP(pid, procSP))1449    return procSP->SetCurrentThread(tid);1450  return INVALID_NUB_THREAD;1451}1452 1453// Dump a string describing a thread's stop reason to the specified file1454// handle1455nub_bool_t DNBThreadGetStopReason(nub_process_t pid, nub_thread_t tid,1456                                  struct DNBThreadStopInfo *stop_info) {1457  MachProcessSP procSP;1458  if (GetProcessSP(pid, procSP))1459    return procSP->GetThreadStoppedReason(tid, stop_info);1460  return false;1461}1462 1463// Return string description for the specified thread.1464//1465// RETURNS: NULL if the thread isn't valid, else a NULL terminated C1466// string from a static buffer that must be copied prior to subsequent1467// calls.1468const char *DNBThreadGetInfo(nub_process_t pid, nub_thread_t tid) {1469  MachProcessSP procSP;1470  if (GetProcessSP(pid, procSP))1471    return procSP->GetThreadInfo(tid);1472  return NULL;1473}1474 1475// Get the thread ID given a thread index.1476nub_thread_t DNBProcessGetThreadAtIndex(nub_process_t pid, size_t thread_idx) {1477  MachProcessSP procSP;1478  if (GetProcessSP(pid, procSP))1479    return procSP->GetThreadAtIndex(thread_idx);1480  return INVALID_NUB_THREAD;1481}1482 1483// Do whatever is needed to sync the thread's register state with it's kernel1484// values.1485nub_bool_t DNBProcessSyncThreadState(nub_process_t pid, nub_thread_t tid) {1486  MachProcessSP procSP;1487  if (GetProcessSP(pid, procSP))1488    return procSP->SyncThreadState(tid);1489  return false;1490}1491 1492nub_addr_t DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid) {1493  MachProcessSP procSP;1494  DNBError err;1495  if (GetProcessSP(pid, procSP))1496    return procSP->Task().GetDYLDAllImageInfosAddress(err);1497  return INVALID_NUB_ADDRESS;1498}1499 1500nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) {1501  MachProcessSP procSP;1502  if (GetProcessSP(pid, procSP)) {1503    procSP->SharedLibrariesUpdated();1504    return true;1505  }1506  return false;1507}1508 1509std::optional<std::string>1510DNBGetDeploymentInfo(nub_process_t pid, bool is_executable,1511                     const struct load_command &lc,1512                     uint64_t load_command_address, uint32_t &major_version,1513                     uint32_t &minor_version, uint32_t &patch_version) {1514  MachProcessSP procSP;1515  if (GetProcessSP(pid, procSP)) {1516    // FIXME: This doesn't return the correct result when xctest (a1517    // macOS binary) is loaded with the macCatalyst dyld platform1518    // override. The image info corrects for this, but qProcessInfo1519    // will return what is in the binary.1520    auto info =1521        procSP->GetDeploymentInfo(lc, load_command_address, is_executable);1522    major_version = info.major_version;1523    minor_version = info.minor_version;1524    patch_version = info.patch_version;1525    // MachProcess::DeploymentInfo has a bool operator to tell whether we have1526    // set the platform.  If that's not true, don't report out the platform:1527    if (!info)1528      return {};1529    return procSP->GetPlatformString(info.platform);1530  }1531  return {};1532}1533 1534// Get the current shared library information for a process. Only return1535// the shared libraries that have changed since the last shared library1536// state changed event if only_changed is non-zero.1537nub_size_t1538DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,1539                               struct DNBExecutableImageInfo **image_infos) {1540  MachProcessSP procSP;1541  if (GetProcessSP(pid, procSP))1542    return procSP->CopyImageInfos(image_infos, only_changed);1543 1544  // If we have no process, then return NULL for the shared library info1545  // and zero for shared library count1546  *image_infos = NULL;1547  return 0;1548}1549 1550uint32_t DNBGetRegisterCPUType() {1551  return DNBArchProtocol::GetRegisterCPUType();1552}1553// Get the register set information for a specific thread.1554const DNBRegisterSetInfo *DNBGetRegisterSetInfo(nub_size_t *num_reg_sets) {1555  return DNBArchProtocol::GetRegisterSetInfo(num_reg_sets);1556}1557 1558// Read a register value by register set and register index.1559nub_bool_t DNBThreadGetRegisterValueByID(nub_process_t pid, nub_thread_t tid,1560                                         uint32_t set, uint32_t reg,1561                                         DNBRegisterValue *value) {1562  MachProcessSP procSP;1563  ::bzero(value, sizeof(DNBRegisterValue));1564  if (GetProcessSP(pid, procSP)) {1565    if (tid != INVALID_NUB_THREAD)1566      return procSP->GetRegisterValue(tid, set, reg, value);1567  }1568  return false;1569}1570 1571nub_bool_t DNBThreadSetRegisterValueByID(nub_process_t pid, nub_thread_t tid,1572                                         uint32_t set, uint32_t reg,1573                                         const DNBRegisterValue *value) {1574  if (tid != INVALID_NUB_THREAD) {1575    MachProcessSP procSP;1576    if (GetProcessSP(pid, procSP))1577      return procSP->SetRegisterValue(tid, set, reg, value);1578  }1579  return false;1580}1581 1582nub_size_t DNBThreadGetRegisterContext(nub_process_t pid, nub_thread_t tid,1583                                       void *buf, size_t buf_len) {1584  MachProcessSP procSP;1585  if (GetProcessSP(pid, procSP)) {1586    if (tid != INVALID_NUB_THREAD)1587      return procSP->GetThreadList().GetRegisterContext(tid, buf, buf_len);1588  }1589  ::bzero(buf, buf_len);1590  return 0;1591}1592 1593nub_size_t DNBThreadSetRegisterContext(nub_process_t pid, nub_thread_t tid,1594                                       const void *buf, size_t buf_len) {1595  MachProcessSP procSP;1596  if (GetProcessSP(pid, procSP)) {1597    if (tid != INVALID_NUB_THREAD)1598      return procSP->GetThreadList().SetRegisterContext(tid, buf, buf_len);1599  }1600  return 0;1601}1602 1603uint32_t DNBThreadSaveRegisterState(nub_process_t pid, nub_thread_t tid) {1604  if (tid != INVALID_NUB_THREAD) {1605    MachProcessSP procSP;1606    if (GetProcessSP(pid, procSP))1607      return procSP->GetThreadList().SaveRegisterState(tid);1608  }1609  return 0;1610}1611nub_bool_t DNBThreadRestoreRegisterState(nub_process_t pid, nub_thread_t tid,1612                                         uint32_t save_id) {1613  if (tid != INVALID_NUB_THREAD) {1614    MachProcessSP procSP;1615    if (GetProcessSP(pid, procSP))1616      return procSP->GetThreadList().RestoreRegisterState(tid, save_id);1617  }1618  return false;1619}1620 1621// Read a register value by name.1622nub_bool_t DNBThreadGetRegisterValueByName(nub_process_t pid, nub_thread_t tid,1623                                           uint32_t reg_set,1624                                           const char *reg_name,1625                                           DNBRegisterValue *value) {1626  MachProcessSP procSP;1627  ::bzero(value, sizeof(DNBRegisterValue));1628  if (GetProcessSP(pid, procSP)) {1629    const struct DNBRegisterSetInfo *set_info;1630    nub_size_t num_reg_sets = 0;1631    set_info = DNBGetRegisterSetInfo(&num_reg_sets);1632    if (set_info) {1633      uint32_t set = reg_set;1634      uint32_t reg;1635      if (set == REGISTER_SET_ALL) {1636        for (set = 1; set < num_reg_sets; ++set) {1637          for (reg = 0; reg < set_info[set].num_registers; ++reg) {1638            if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)1639              return procSP->GetRegisterValue(tid, set, reg, value);1640          }1641        }1642      } else {1643        for (reg = 0; reg < set_info[set].num_registers; ++reg) {1644          if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)1645            return procSP->GetRegisterValue(tid, set, reg, value);1646        }1647      }1648    }1649  }1650  return false;1651}1652 1653// Read a register set and register number from the register name.1654nub_bool_t DNBGetRegisterInfoByName(const char *reg_name,1655                                    DNBRegisterInfo *info) {1656  const struct DNBRegisterSetInfo *set_info;1657  nub_size_t num_reg_sets = 0;1658  set_info = DNBGetRegisterSetInfo(&num_reg_sets);1659  if (set_info) {1660    uint32_t set, reg;1661    for (set = 1; set < num_reg_sets; ++set) {1662      for (reg = 0; reg < set_info[set].num_registers; ++reg) {1663        if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) {1664          *info = set_info[set].registers[reg];1665          return true;1666        }1667      }1668    }1669 1670    for (set = 1; set < num_reg_sets; ++set) {1671      uint32_t reg;1672      for (reg = 0; reg < set_info[set].num_registers; ++reg) {1673        if (set_info[set].registers[reg].alt == NULL)1674          continue;1675 1676        if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0) {1677          *info = set_info[set].registers[reg];1678          return true;1679        }1680      }1681    }1682  }1683 1684  ::bzero(info, sizeof(DNBRegisterInfo));1685  return false;1686}1687 1688// Set the name to address callback function that this nub can use1689// for any name to address lookups that are needed.1690nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,1691                                              DNBCallbackNameToAddress callback,1692                                              void *baton) {1693  MachProcessSP procSP;1694  if (GetProcessSP(pid, procSP)) {1695    procSP->SetNameToAddressCallback(callback, baton);1696    return true;1697  }1698  return false;1699}1700 1701// Set the name to address callback function that this nub can use1702// for any name to address lookups that are needed.1703nub_bool_t DNBProcessSetSharedLibraryInfoCallback(1704    nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback,1705    void *baton) {1706  MachProcessSP procSP;1707  if (GetProcessSP(pid, procSP)) {1708    procSP->SetSharedLibraryInfoCallback(callback, baton);1709    return true;1710  }1711  return false;1712}1713 1714nub_addr_t DNBProcessLookupAddress(nub_process_t pid, const char *name,1715                                   const char *shlib) {1716  MachProcessSP procSP;1717  if (GetProcessSP(pid, procSP)) {1718    return procSP->LookupSymbol(name, shlib);1719  }1720  return INVALID_NUB_ADDRESS;1721}1722 1723nub_size_t DNBProcessGetAvailableSTDOUT(nub_process_t pid, char *buf,1724                                        nub_size_t buf_size) {1725  MachProcessSP procSP;1726  if (GetProcessSP(pid, procSP))1727    return procSP->GetAvailableSTDOUT(buf, buf_size);1728  return 0;1729}1730 1731nub_size_t DNBProcessGetAvailableSTDERR(nub_process_t pid, char *buf,1732                                        nub_size_t buf_size) {1733  MachProcessSP procSP;1734  if (GetProcessSP(pid, procSP))1735    return procSP->GetAvailableSTDERR(buf, buf_size);1736  return 0;1737}1738 1739nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,1740                                             nub_size_t buf_size) {1741  MachProcessSP procSP;1742  if (GetProcessSP(pid, procSP))1743    return procSP->GetAsyncProfileData(buf, buf_size);1744  return 0;1745}1746 1747nub_size_t DNBProcessGetStopCount(nub_process_t pid) {1748  MachProcessSP procSP;1749  if (GetProcessSP(pid, procSP))1750    return procSP->StopCount();1751  return 0;1752}1753 1754uint32_t DNBProcessGetCPUType(nub_process_t pid) {1755  MachProcessSP procSP;1756  if (GetProcessSP(pid, procSP))1757    return procSP->GetCPUType();1758  return 0;1759}1760 1761nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path,1762                                    size_t resolved_path_size) {1763  if (path == NULL || path[0] == '\0')1764    return false;1765 1766  char max_path[PATH_MAX];1767  std::string result;1768  CFString::GlobPath(path, result);1769 1770  if (result.empty())1771    result = path;1772 1773  struct stat path_stat;1774  if (::stat(path, &path_stat) == 0) {1775    if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {1776      CFBundle bundle(path);1777      CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());1778      if (url.get()) {1779        if (::CFURLGetFileSystemRepresentation(1780                url.get(), true, (UInt8 *)resolved_path, resolved_path_size))1781          return true;1782      }1783    }1784  }1785 1786  if (realpath(path, max_path)) {1787    // Found the path relatively...1788    ::strlcpy(resolved_path, max_path, resolved_path_size);1789    return strlen(resolved_path) + 1 < resolved_path_size;1790  } else {1791    // Not a relative path, check the PATH environment variable if the1792    const char *PATH = getenv("PATH");1793    if (PATH) {1794      const char *curr_path_start = PATH;1795      const char *curr_path_end;1796      while (curr_path_start && *curr_path_start) {1797        curr_path_end = strchr(curr_path_start, ':');1798        if (curr_path_end == NULL) {1799          result.assign(curr_path_start);1800          curr_path_start = NULL;1801        } else if (curr_path_end > curr_path_start) {1802          size_t len = curr_path_end - curr_path_start;1803          result.assign(curr_path_start, len);1804          curr_path_start += len + 1;1805        } else1806          break;1807 1808        result += '/';1809        result += path;1810        struct stat s;1811        if (stat(result.c_str(), &s) == 0) {1812          ::strlcpy(resolved_path, result.c_str(), resolved_path_size);1813          return result.size() + 1 < resolved_path_size;1814        }1815      }1816    }1817  }1818  return false;1819}1820 1821bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch) {1822  return MachProcess::GetOSVersionNumbers(major, minor, patch);1823}1824 1825std::string DNBGetMacCatalystVersionString() {1826  return MachProcess::GetMacCatalystVersionString();1827}1828 1829void DNBInitialize() {1830  DNBLogThreadedIf(LOG_PROCESS, "DNBInitialize ()");1831#if defined(__i386__) || defined(__x86_64__)1832  DNBArchImplX86_64::Initialize();1833#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)1834  DNBArchMachARM64::Initialize();1835#endif1836}1837 1838void DNBTerminate() {}1839 1840nub_bool_t DNBSetArchitecture(const char *arch) {1841  if (arch && arch[0]) {1842    if (strcasecmp(arch, "i386") == 0)1843      return DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);1844    else if (strcasecmp(arch, "x86_64") == 0)1845      return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64,1846                                              CPU_SUBTYPE_X86_64_ALL);1847    else if (strcasecmp(arch, "x86_64h") == 0)1848      return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64,1849                                              CPU_SUBTYPE_X86_64_H);1850    else if (strstr(arch, "arm64_32") == arch ||1851             strstr(arch, "aarch64_32") == arch)1852      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32);1853    else if (strstr(arch, "arm64e") == arch)1854      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,1855                                              CPU_SUBTYPE_ARM64E);1856    else if (strstr(arch, "arm64") == arch || strstr(arch, "aarch64") == arch)1857      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,1858                                              CPU_SUBTYPE_ARM64_ALL);1859    else if (strstr(arch, "armv8") == arch)1860      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,1861                                              CPU_SUBTYPE_ARM64_V8);1862    else if (strstr(arch, "armv7em") == arch)1863      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1864                                              CPU_SUBTYPE_ARM_V7EM);1865    else if (strstr(arch, "armv7m") == arch)1866      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1867                                              CPU_SUBTYPE_ARM_V7M);1868    else if (strstr(arch, "armv7k") == arch)1869      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1870                                              CPU_SUBTYPE_ARM_V7K);1871    else if (strstr(arch, "armv7s") == arch)1872      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1873                                              CPU_SUBTYPE_ARM_V7S);1874    else if (strstr(arch, "armv7") == arch)1875      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7);1876    else if (strstr(arch, "armv6m") == arch)1877      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1878                                              CPU_SUBTYPE_ARM_V6M);1879    else if (strstr(arch, "armv6") == arch)1880      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6);1881    else if (strstr(arch, "armv5") == arch)1882      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1883                                              CPU_SUBTYPE_ARM_V5TEJ);1884    else if (strstr(arch, "armv4t") == arch)1885      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1886                                              CPU_SUBTYPE_ARM_V4T);1887    else if (strstr(arch, "arm") == arch)1888      return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,1889                                              CPU_SUBTYPE_ARM_ALL);1890  }1891  return false;1892}1893 1894bool DNBDebugserverIsTranslated() {1895  int ret = 0;1896  size_t size = sizeof(ret);1897  if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1)1898    return false;1899  return ret == 1;1900}1901 1902bool DNBGetAddressingBits(uint32_t &addressing_bits) {1903  static uint32_t g_addressing_bits = 0;1904  static std::once_flag g_once_flag;1905  std::call_once(g_once_flag, [&](){1906    size_t len = sizeof(uint32_t);1907    if (::sysctlbyname("machdep.virtual_address_size", &g_addressing_bits, &len,1908                       NULL, 0) != 0) {1909      g_addressing_bits = 0;1910    }1911  });1912 1913  addressing_bits = g_addressing_bits;1914 1915  return addressing_bits > 0;1916}1917 1918nub_process_t DNBGetParentProcessID(nub_process_t child_pid) {1919  return MachProcess::GetParentProcessID(child_pid);1920}1921 1922bool DNBProcessIsBeingDebugged(nub_process_t pid) {1923  return MachProcess::ProcessIsBeingDebugged(pid);1924}1925