
*** to conform to clang-format’s LLVM style. This kind of mass change has *** two obvious implications: Firstly, merging this particular commit into a downstream fork may be a huge effort. Alternatively, it may be worth merging all changes up to this commit, performing the same reformatting operation locally, and then discarding the merge for this particular commit. The commands used to accomplish this reformatting were as follows (with current working directory as the root of the repository): find . \( -iname "*.c" -or -iname "*.cpp" -or -iname "*.h" -or -iname "*.mm" \) -exec clang-format -i {} + find . -iname "*.py" -exec autopep8 --in-place --aggressive --aggressive {} + ; The version of clang-format used was 3.9.0, and autopep8 was 1.2.4. Secondly, “blame” style tools will generally point to this commit instead of a meaningful prior commit. There are alternatives available that will attempt to look through this change and find the appropriate prior commit. YMMV. llvm-svn: 280751
1766 lines
58 KiB
C++
1766 lines
58 KiB
C++
//===-- DNB.cpp -------------------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Created by Greg Clayton on 3/23/07.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DNB.h"
|
|
#include <inttypes.h>
|
|
#include <libproc.h>
|
|
#include <map>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <vector>
|
|
|
|
#if defined(__APPLE__)
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#endif
|
|
|
|
#define TRY_KQUEUE 1
|
|
|
|
#ifdef TRY_KQUEUE
|
|
#include <sys/event.h>
|
|
#include <sys/time.h>
|
|
#ifdef NOTE_EXIT_DETAIL
|
|
#define USE_KQUEUE
|
|
#endif
|
|
#endif
|
|
|
|
#include "CFBundle.h"
|
|
#include "CFString.h"
|
|
#include "DNBDataRef.h"
|
|
#include "DNBLog.h"
|
|
#include "DNBThreadResumeActions.h"
|
|
#include "DNBTimer.h"
|
|
#include "MacOSX/DarwinLog/DarwinLogCollector.h"
|
|
#include "MacOSX/Genealogy.h"
|
|
#include "MacOSX/MachProcess.h"
|
|
#include "MacOSX/MachTask.h"
|
|
#include "MacOSX/ThreadInfo.h"
|
|
|
|
typedef std::shared_ptr<MachProcess> MachProcessSP;
|
|
typedef std::map<nub_process_t, MachProcessSP> ProcessMap;
|
|
typedef ProcessMap::iterator ProcessMapIter;
|
|
typedef ProcessMap::const_iterator ProcessMapConstIter;
|
|
|
|
size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos);
|
|
static size_t
|
|
GetAllInfosMatchingName(const char *process_name,
|
|
std::vector<struct kinfo_proc> &matching_proc_infos);
|
|
|
|
//----------------------------------------------------------------------
|
|
// A Thread safe singleton to get a process map pointer.
|
|
//
|
|
// Returns a pointer to the existing process map, or a pointer to a
|
|
// newly created process map if CAN_CREATE is non-zero.
|
|
//----------------------------------------------------------------------
|
|
static ProcessMap *GetProcessMap(bool can_create) {
|
|
static ProcessMap *g_process_map_ptr = NULL;
|
|
|
|
if (can_create && g_process_map_ptr == NULL) {
|
|
static pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
PTHREAD_MUTEX_LOCKER(locker, &g_process_map_mutex);
|
|
if (g_process_map_ptr == NULL)
|
|
g_process_map_ptr = new ProcessMap;
|
|
}
|
|
return g_process_map_ptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Add PID to the shared process pointer map.
|
|
//
|
|
// Return non-zero value if we succeed in adding the process to the map.
|
|
// The only time this should fail is if we run out of memory and can't
|
|
// allocate a ProcessMap.
|
|
//----------------------------------------------------------------------
|
|
static nub_bool_t AddProcessToMap(nub_process_t pid, MachProcessSP &procSP) {
|
|
ProcessMap *process_map = GetProcessMap(true);
|
|
if (process_map) {
|
|
process_map->insert(std::make_pair(pid, procSP));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Remove the shared pointer for PID from the process map.
|
|
//
|
|
// Returns the number of items removed from the process map.
|
|
//----------------------------------------------------------------------
|
|
// static size_t
|
|
// RemoveProcessFromMap (nub_process_t pid)
|
|
//{
|
|
// ProcessMap* process_map = GetProcessMap(false);
|
|
// if (process_map)
|
|
// {
|
|
// return process_map->erase(pid);
|
|
// }
|
|
// return 0;
|
|
//}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Get the shared pointer for PID from the existing process map.
|
|
//
|
|
// Returns true if we successfully find a shared pointer to a
|
|
// MachProcess object.
|
|
//----------------------------------------------------------------------
|
|
static nub_bool_t GetProcessSP(nub_process_t pid, MachProcessSP &procSP) {
|
|
ProcessMap *process_map = GetProcessMap(false);
|
|
if (process_map != NULL) {
|
|
ProcessMapIter pos = process_map->find(pid);
|
|
if (pos != process_map->end()) {
|
|
procSP = pos->second;
|
|
return true;
|
|
}
|
|
}
|
|
procSP.reset();
|
|
return false;
|
|
}
|
|
|
|
#ifdef USE_KQUEUE
|
|
void *kqueue_thread(void *arg) {
|
|
int kq_id = (int)(intptr_t)arg;
|
|
|
|
#if defined(__APPLE__)
|
|
pthread_setname_np("kqueue thread");
|
|
#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
|
struct sched_param thread_param;
|
|
int thread_sched_policy;
|
|
if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
|
|
&thread_param) == 0) {
|
|
thread_param.sched_priority = 47;
|
|
pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
struct kevent death_event;
|
|
while (1) {
|
|
int n_events = kevent(kq_id, NULL, 0, &death_event, 1, NULL);
|
|
if (n_events == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
else {
|
|
DNBLogError("kqueue failed with error: (%d): %s", errno,
|
|
strerror(errno));
|
|
return NULL;
|
|
}
|
|
} else if (death_event.flags & EV_ERROR) {
|
|
int error_no = static_cast<int>(death_event.data);
|
|
const char *error_str = strerror(error_no);
|
|
if (error_str == NULL)
|
|
error_str = "Unknown error";
|
|
DNBLogError("Failed to initialize kqueue event: (%d): %s", error_no,
|
|
error_str);
|
|
return NULL;
|
|
} else {
|
|
int status;
|
|
const pid_t pid = (pid_t)death_event.ident;
|
|
const pid_t child_pid = waitpid(pid, &status, 0);
|
|
|
|
bool exited = false;
|
|
int signal = 0;
|
|
int exit_status = 0;
|
|
if (WIFSTOPPED(status)) {
|
|
signal = WSTOPSIG(status);
|
|
DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> STOPPED (signal = %i)",
|
|
child_pid, signal);
|
|
} else if (WIFEXITED(status)) {
|
|
exit_status = WEXITSTATUS(status);
|
|
exited = true;
|
|
DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> EXITED (status = %i)",
|
|
child_pid, exit_status);
|
|
} else if (WIFSIGNALED(status)) {
|
|
signal = WTERMSIG(status);
|
|
if (child_pid == abs(pid)) {
|
|
DNBLogThreadedIf(LOG_PROCESS,
|
|
"waitpid (%i) -> SIGNALED and EXITED (signal = %i)",
|
|
child_pid, signal);
|
|
char exit_info[64];
|
|
::snprintf(exit_info, sizeof(exit_info),
|
|
"Terminated due to signal %i", signal);
|
|
DNBProcessSetExitInfo(child_pid, exit_info);
|
|
exited = true;
|
|
exit_status = INT8_MAX;
|
|
} else {
|
|
DNBLogThreadedIf(LOG_PROCESS,
|
|
"waitpid (%i) -> SIGNALED (signal = %i)", child_pid,
|
|
signal);
|
|
}
|
|
}
|
|
|
|
if (exited) {
|
|
if (death_event.data & NOTE_EXIT_MEMORY)
|
|
DNBProcessSetExitInfo(child_pid, "Terminated due to memory issue");
|
|
else if (death_event.data & NOTE_EXIT_DECRYPTFAIL)
|
|
DNBProcessSetExitInfo(child_pid, "Terminated due to decrypt failure");
|
|
else if (death_event.data & NOTE_EXIT_CSERROR)
|
|
DNBProcessSetExitInfo(child_pid,
|
|
"Terminated due to code signing error");
|
|
|
|
DNBLogThreadedIf(
|
|
LOG_PROCESS,
|
|
"waitpid_process_thread (): setting exit status for pid = %i to %i",
|
|
child_pid, exit_status);
|
|
DNBProcessSetExitStatus(child_pid, status);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool spawn_kqueue_thread(pid_t pid) {
|
|
pthread_t thread;
|
|
int kq_id;
|
|
|
|
kq_id = kqueue();
|
|
if (kq_id == -1) {
|
|
DNBLogError("Could not get kqueue for pid = %i.", pid);
|
|
return false;
|
|
}
|
|
|
|
struct kevent reg_event;
|
|
|
|
EV_SET(®_event, pid, EVFILT_PROC, EV_ADD,
|
|
NOTE_EXIT | NOTE_EXITSTATUS | NOTE_EXIT_DETAIL, 0, NULL);
|
|
// Register the event:
|
|
int result = kevent(kq_id, ®_event, 1, NULL, 0, NULL);
|
|
if (result != 0) {
|
|
DNBLogError(
|
|
"Failed to register kqueue NOTE_EXIT event for pid %i, error: %d.", pid,
|
|
result);
|
|
return false;
|
|
}
|
|
|
|
int ret =
|
|
::pthread_create(&thread, NULL, kqueue_thread, (void *)(intptr_t)kq_id);
|
|
|
|
// pthread_create returns 0 if successful
|
|
if (ret == 0) {
|
|
::pthread_detach(thread);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif // #if USE_KQUEUE
|
|
|
|
static void *waitpid_thread(void *arg) {
|
|
const pid_t pid = (pid_t)(intptr_t)arg;
|
|
int status;
|
|
|
|
#if defined(__APPLE__)
|
|
pthread_setname_np("waitpid thread");
|
|
#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
|
struct sched_param thread_param;
|
|
int thread_sched_policy;
|
|
if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
|
|
&thread_param) == 0) {
|
|
thread_param.sched_priority = 47;
|
|
pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
while (1) {
|
|
pid_t child_pid = waitpid(pid, &status, 0);
|
|
DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): waitpid (pid = %i, "
|
|
"&status, 0) => %i, status = %i, errno = %i",
|
|
pid, child_pid, status, errno);
|
|
|
|
if (child_pid < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
break;
|
|
} else {
|
|
if (WIFSTOPPED(status)) {
|
|
continue;
|
|
} else // if (WIFEXITED(status) || WIFSIGNALED(status))
|
|
{
|
|
DNBLogThreadedIf(
|
|
LOG_PROCESS,
|
|
"waitpid_thread (): setting exit status for pid = %i to %i",
|
|
child_pid, status);
|
|
DNBProcessSetExitStatus(child_pid, status);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We should never exit as long as our child process is alive, so if we
|
|
// do something else went wrong and we should exit...
|
|
DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): main loop exited, setting "
|
|
"exit status to an invalid value (-1) for pid "
|
|
"%i",
|
|
pid);
|
|
DNBProcessSetExitStatus(pid, -1);
|
|
return NULL;
|
|
}
|
|
static bool spawn_waitpid_thread(pid_t pid) {
|
|
#ifdef USE_KQUEUE
|
|
bool success = spawn_kqueue_thread(pid);
|
|
if (success)
|
|
return true;
|
|
#endif
|
|
|
|
pthread_t thread;
|
|
int ret =
|
|
::pthread_create(&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);
|
|
// pthread_create returns 0 if successful
|
|
if (ret == 0) {
|
|
::pthread_detach(thread);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_process_t DNBProcessLaunch(
|
|
const char *path, char const *argv[], const char *envp[],
|
|
const char *working_directory, // NULL => don't change, non-NULL => set
|
|
// working directory for inferior to this
|
|
const char *stdin_path, const char *stdout_path, const char *stderr_path,
|
|
bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr,
|
|
const char *event_data, char *err_str, size_t err_len) {
|
|
DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv = %p, envp = %p, "
|
|
"working_dir=%s, stdin=%s, stdout=%s, "
|
|
"stderr=%s, no-stdio=%i, launch_flavor = %u, "
|
|
"disable_aslr = %d, err = %p, err_len = "
|
|
"%llu) called...",
|
|
__FUNCTION__, path, static_cast<void *>(argv),
|
|
static_cast<void *>(envp), working_directory, stdin_path,
|
|
stdout_path, stderr_path, no_stdio, launch_flavor,
|
|
disable_aslr, static_cast<void *>(err_str),
|
|
static_cast<uint64_t>(err_len));
|
|
|
|
if (err_str && err_len > 0)
|
|
err_str[0] = '\0';
|
|
struct stat path_stat;
|
|
if (::stat(path, &path_stat) == -1) {
|
|
char stat_error[256];
|
|
::strerror_r(errno, stat_error, sizeof(stat_error));
|
|
snprintf(err_str, err_len, "%s (%s)", stat_error, path);
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
|
|
MachProcessSP processSP(new MachProcess);
|
|
if (processSP.get()) {
|
|
DNBError launch_err;
|
|
pid_t pid = processSP->LaunchForDebug(path, argv, envp, working_directory,
|
|
stdin_path, stdout_path, stderr_path,
|
|
no_stdio, launch_flavor, disable_aslr,
|
|
event_data, launch_err);
|
|
if (err_str) {
|
|
*err_str = '\0';
|
|
if (launch_err.Fail()) {
|
|
const char *launch_err_str = launch_err.AsString();
|
|
if (launch_err_str) {
|
|
strncpy(err_str, launch_err_str, err_len - 1);
|
|
err_str[err_len - 1] =
|
|
'\0'; // Make sure the error string is terminated
|
|
}
|
|
}
|
|
}
|
|
|
|
DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid);
|
|
|
|
if (pid != INVALID_NUB_PROCESS) {
|
|
// Spawn a thread to reap our child inferior process...
|
|
spawn_waitpid_thread(pid);
|
|
|
|
if (processSP->Task().TaskPortForProcessID(launch_err) == TASK_NULL) {
|
|
// We failed to get the task for our process ID which is bad.
|
|
// Kill our process otherwise it will be stopped at the entry
|
|
// point and get reparented to someone else and never go away.
|
|
DNBLog("Could not get task port for process, sending SIGKILL and "
|
|
"exiting.");
|
|
kill(SIGKILL, pid);
|
|
|
|
if (err_str && err_len > 0) {
|
|
if (launch_err.AsString()) {
|
|
::snprintf(err_str, err_len,
|
|
"failed to get the task for process %i (%s)", pid,
|
|
launch_err.AsString());
|
|
} else {
|
|
::snprintf(err_str, err_len,
|
|
"failed to get the task for process %i", pid);
|
|
}
|
|
}
|
|
} else {
|
|
bool res = AddProcessToMap(pid, processSP);
|
|
UNUSED_IF_ASSERT_DISABLED(res);
|
|
assert(res && "Couldn't add process to map!");
|
|
return pid;
|
|
}
|
|
}
|
|
}
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
|
|
// If there is one process with a given name, return the pid for that process.
|
|
nub_process_t DNBProcessGetPIDByName(const char *name) {
|
|
std::vector<struct kinfo_proc> matching_proc_infos;
|
|
size_t num_matching_proc_infos =
|
|
GetAllInfosMatchingName(name, matching_proc_infos);
|
|
if (num_matching_proc_infos == 1) {
|
|
return matching_proc_infos[0].kp_proc.p_pid;
|
|
}
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
|
|
nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout,
|
|
char *err_str, size_t err_len) {
|
|
if (err_str && err_len > 0)
|
|
err_str[0] = '\0';
|
|
std::vector<struct kinfo_proc> matching_proc_infos;
|
|
size_t num_matching_proc_infos =
|
|
GetAllInfosMatchingName(name, matching_proc_infos);
|
|
if (num_matching_proc_infos == 0) {
|
|
DNBLogError("error: no processes match '%s'\n", name);
|
|
return INVALID_NUB_PROCESS;
|
|
} else if (num_matching_proc_infos > 1) {
|
|
DNBLogError("error: %llu processes match '%s':\n",
|
|
(uint64_t)num_matching_proc_infos, name);
|
|
size_t i;
|
|
for (i = 0; i < num_matching_proc_infos; ++i)
|
|
DNBLogError("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid,
|
|
matching_proc_infos[i].kp_proc.p_comm);
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
|
|
return DNBProcessAttach(matching_proc_infos[0].kp_proc.p_pid, timeout,
|
|
err_str, err_len);
|
|
}
|
|
|
|
nub_process_t DNBProcessAttach(nub_process_t attach_pid,
|
|
struct timespec *timeout, char *err_str,
|
|
size_t err_len) {
|
|
if (err_str && err_len > 0)
|
|
err_str[0] = '\0';
|
|
|
|
pid_t pid = INVALID_NUB_PROCESS;
|
|
MachProcessSP processSP(new MachProcess);
|
|
if (processSP.get()) {
|
|
DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...",
|
|
attach_pid);
|
|
pid = processSP->AttachForDebug(attach_pid, err_str, err_len);
|
|
|
|
if (pid != INVALID_NUB_PROCESS) {
|
|
bool res = AddProcessToMap(pid, processSP);
|
|
UNUSED_IF_ASSERT_DISABLED(res);
|
|
assert(res && "Couldn't add process to map!");
|
|
spawn_waitpid_thread(pid);
|
|
}
|
|
}
|
|
|
|
while (pid != INVALID_NUB_PROCESS) {
|
|
// Wait for process to start up and hit entry point
|
|
DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
|
|
"eEventProcessRunningStateChanged | "
|
|
"eEventProcessStoppedStateChanged, true, "
|
|
"INFINITE)...",
|
|
__FUNCTION__, pid);
|
|
nub_event_t set_events =
|
|
DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |
|
|
eEventProcessStoppedStateChanged,
|
|
true, timeout);
|
|
|
|
DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
|
|
"eEventProcessRunningStateChanged | "
|
|
"eEventProcessStoppedStateChanged, true, "
|
|
"INFINITE) => 0x%8.8x",
|
|
__FUNCTION__, pid, set_events);
|
|
|
|
if (set_events == 0) {
|
|
if (err_str && err_len > 0)
|
|
snprintf(err_str, err_len, "operation timed out");
|
|
pid = INVALID_NUB_PROCESS;
|
|
} else {
|
|
if (set_events & (eEventProcessRunningStateChanged |
|
|
eEventProcessStoppedStateChanged)) {
|
|
nub_state_t pid_state = DNBProcessGetState(pid);
|
|
DNBLogThreadedIf(
|
|
LOG_PROCESS,
|
|
"%s process %4.4x state changed (eEventProcessStateChanged): %s",
|
|
__FUNCTION__, pid, DNBStateAsString(pid_state));
|
|
|
|
switch (pid_state) {
|
|
case eStateInvalid:
|
|
case eStateUnloaded:
|
|
case eStateAttaching:
|
|
case eStateLaunching:
|
|
case eStateSuspended:
|
|
break; // Ignore
|
|
|
|
case eStateRunning:
|
|
case eStateStepping:
|
|
// Still waiting to stop at entry point...
|
|
break;
|
|
|
|
case eStateStopped:
|
|
case eStateCrashed:
|
|
return pid;
|
|
|
|
case eStateDetached:
|
|
case eStateExited:
|
|
if (err_str && err_len > 0)
|
|
snprintf(err_str, err_len, "process exited");
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
}
|
|
|
|
DNBProcessResetEvents(pid, set_events);
|
|
}
|
|
}
|
|
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
|
|
size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos) {
|
|
size_t size = 0;
|
|
int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
|
|
u_int namelen = sizeof(name) / sizeof(int);
|
|
int err;
|
|
|
|
// Try to find out how many processes are around so we can
|
|
// size the buffer appropriately. sysctl's man page specifically suggests
|
|
// this approach, and says it returns a bit larger size than needed to
|
|
// handle any new processes created between then and now.
|
|
|
|
err = ::sysctl(name, namelen, NULL, &size, NULL, 0);
|
|
|
|
if ((err < 0) && (err != ENOMEM)) {
|
|
proc_infos.clear();
|
|
perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");
|
|
return 0;
|
|
}
|
|
|
|
// Increase the size of the buffer by a few processes in case more have
|
|
// been spawned
|
|
proc_infos.resize(size / sizeof(struct kinfo_proc));
|
|
size = proc_infos.size() *
|
|
sizeof(struct kinfo_proc); // Make sure we don't exceed our resize...
|
|
err = ::sysctl(name, namelen, &proc_infos[0], &size, NULL, 0);
|
|
if (err < 0) {
|
|
proc_infos.clear();
|
|
return 0;
|
|
}
|
|
|
|
// Trim down our array to fit what we actually got back
|
|
proc_infos.resize(size / sizeof(struct kinfo_proc));
|
|
return proc_infos.size();
|
|
}
|
|
|
|
static size_t
|
|
GetAllInfosMatchingName(const char *full_process_name,
|
|
std::vector<struct kinfo_proc> &matching_proc_infos) {
|
|
|
|
matching_proc_infos.clear();
|
|
if (full_process_name && full_process_name[0]) {
|
|
// We only get the process name, not the full path, from the proc_info. So
|
|
// just take the
|
|
// base name of the process name...
|
|
const char *process_name;
|
|
process_name = strrchr(full_process_name, '/');
|
|
if (process_name == NULL)
|
|
process_name = full_process_name;
|
|
else
|
|
process_name++;
|
|
|
|
const size_t process_name_len = strlen(process_name);
|
|
std::vector<struct kinfo_proc> proc_infos;
|
|
const size_t num_proc_infos = GetAllInfos(proc_infos);
|
|
if (num_proc_infos > 0) {
|
|
uint32_t i;
|
|
for (i = 0; i < num_proc_infos; i++) {
|
|
// Skip zombie processes and processes with unset status
|
|
if (proc_infos[i].kp_proc.p_stat == 0 ||
|
|
proc_infos[i].kp_proc.p_stat == SZOMB)
|
|
continue;
|
|
|
|
// Check for process by name. We only check the first MAXCOMLEN
|
|
// chars as that is all that kp_proc.p_comm holds.
|
|
|
|
if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm,
|
|
MAXCOMLEN) == 0) {
|
|
if (process_name_len > MAXCOMLEN) {
|
|
// We found a matching process name whose first MAXCOMLEN
|
|
// characters match, but there is more to the name than
|
|
// this. We need to get the full process name. Use proc_pidpath,
|
|
// which will get
|
|
// us the full path to the executed process.
|
|
|
|
char proc_path_buf[PATH_MAX];
|
|
|
|
int return_val = proc_pidpath(proc_infos[i].kp_proc.p_pid,
|
|
proc_path_buf, PATH_MAX);
|
|
if (return_val > 0) {
|
|
// Okay, now search backwards from that to see if there is a
|
|
// slash in the name. Note, even though we got all the args we
|
|
// don't care
|
|
// because the list data is just a bunch of concatenated null
|
|
// terminated strings
|
|
// so strrchr will start from the end of argv0.
|
|
|
|
const char *argv_basename = strrchr(proc_path_buf, '/');
|
|
if (argv_basename) {
|
|
// Skip the '/'
|
|
++argv_basename;
|
|
} else {
|
|
// We didn't find a directory delimiter in the process argv[0],
|
|
// just use what was in there
|
|
argv_basename = proc_path_buf;
|
|
}
|
|
|
|
if (argv_basename) {
|
|
if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0) {
|
|
matching_proc_infos.push_back(proc_infos[i]);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// We found a matching process, add it to our list
|
|
matching_proc_infos.push_back(proc_infos[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// return the newly added matches.
|
|
return matching_proc_infos.size();
|
|
}
|
|
|
|
nub_process_t DNBProcessAttachWait(
|
|
const char *waitfor_process_name, nub_launch_flavor_t launch_flavor,
|
|
bool ignore_existing, struct timespec *timeout_abstime,
|
|
useconds_t waitfor_interval, char *err_str, size_t err_len,
|
|
DNBShouldCancelCallback should_cancel_callback, void *callback_data) {
|
|
DNBError prepare_error;
|
|
std::vector<struct kinfo_proc> exclude_proc_infos;
|
|
size_t num_exclude_proc_infos;
|
|
|
|
// If the PrepareForAttach returns a valid token, use MachProcess to check
|
|
// for the process, otherwise scan the process table.
|
|
|
|
const void *attach_token = MachProcess::PrepareForAttach(
|
|
waitfor_process_name, launch_flavor, true, prepare_error);
|
|
|
|
if (prepare_error.Fail()) {
|
|
DNBLogError("Error in PrepareForAttach: %s", prepare_error.AsString());
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
|
|
if (attach_token == NULL) {
|
|
if (ignore_existing)
|
|
num_exclude_proc_infos =
|
|
GetAllInfosMatchingName(waitfor_process_name, exclude_proc_infos);
|
|
else
|
|
num_exclude_proc_infos = 0;
|
|
}
|
|
|
|
DNBLogThreadedIf(LOG_PROCESS, "Waiting for '%s' to appear...\n",
|
|
waitfor_process_name);
|
|
|
|
// Loop and try to find the process by name
|
|
nub_process_t waitfor_pid = INVALID_NUB_PROCESS;
|
|
|
|
while (waitfor_pid == INVALID_NUB_PROCESS) {
|
|
if (attach_token != NULL) {
|
|
nub_process_t pid;
|
|
pid = MachProcess::CheckForProcess(attach_token, launch_flavor);
|
|
if (pid != INVALID_NUB_PROCESS) {
|
|
waitfor_pid = pid;
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
// Get the current process list, and check for matches that
|
|
// aren't in our original list. If anyone wants to attach
|
|
// to an existing process by name, they should do it with
|
|
// --attach=PROCNAME. Else we will wait for the first matching
|
|
// process that wasn't in our exclusion list.
|
|
std::vector<struct kinfo_proc> proc_infos;
|
|
const size_t num_proc_infos =
|
|
GetAllInfosMatchingName(waitfor_process_name, proc_infos);
|
|
for (size_t i = 0; i < num_proc_infos; i++) {
|
|
nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;
|
|
for (size_t j = 0; j < num_exclude_proc_infos; j++) {
|
|
if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid) {
|
|
// This process was in our exclusion list, don't use it.
|
|
curr_pid = INVALID_NUB_PROCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we didn't find CURR_PID in our exclusion list, then use it.
|
|
if (curr_pid != INVALID_NUB_PROCESS) {
|
|
// We found our process!
|
|
waitfor_pid = curr_pid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we haven't found our process yet, check for a timeout
|
|
// and then sleep for a bit until we poll again.
|
|
if (waitfor_pid == INVALID_NUB_PROCESS) {
|
|
if (timeout_abstime != NULL) {
|
|
// Check to see if we have a waitfor-duration option that
|
|
// has timed out?
|
|
if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime)) {
|
|
if (err_str && err_len > 0)
|
|
snprintf(err_str, err_len, "operation timed out");
|
|
DNBLogError("error: waiting for process '%s' timed out.\n",
|
|
waitfor_process_name);
|
|
return INVALID_NUB_PROCESS;
|
|
}
|
|
}
|
|
|
|
// Call the should cancel callback as well...
|
|
|
|
if (should_cancel_callback != NULL &&
|
|
should_cancel_callback(callback_data)) {
|
|
DNBLogThreadedIf(
|
|
LOG_PROCESS,
|
|
"DNBProcessAttachWait cancelled by should_cancel callback.");
|
|
waitfor_pid = INVALID_NUB_PROCESS;
|
|
break;
|
|
}
|
|
|
|
::usleep(waitfor_interval); // Sleep for WAITFOR_INTERVAL, then poll again
|
|
}
|
|
}
|
|
|
|
if (waitfor_pid != INVALID_NUB_PROCESS) {
|
|
DNBLogThreadedIf(LOG_PROCESS, "Attaching to %s with pid %i...\n",
|
|
waitfor_process_name, waitfor_pid);
|
|
waitfor_pid =
|
|
DNBProcessAttach(waitfor_pid, timeout_abstime, err_str, err_len);
|
|
}
|
|
|
|
bool success = waitfor_pid != INVALID_NUB_PROCESS;
|
|
MachProcess::CleanupAfterAttach(attach_token, launch_flavor, success,
|
|
prepare_error);
|
|
|
|
return waitfor_pid;
|
|
}
|
|
|
|
nub_bool_t DNBProcessDetach(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
const bool remove = true;
|
|
DNBLogThreaded(
|
|
"Disabling breakpoints and watchpoints, and detaching from %d.", pid);
|
|
procSP->DisableAllBreakpoints(remove);
|
|
procSP->DisableAllWatchpoints(remove);
|
|
return procSP->Detach();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBProcessKill(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->Kill();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBProcessSignal(nub_process_t pid, int signal) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->Signal(signal);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBProcessInterrupt(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->Interrupt();
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBProcessSendEvent(nub_process_t pid, const char *event) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
// FIXME: Do something with the error...
|
|
DNBError send_error;
|
|
return procSP->SendEvent(event, send_error);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBProcessIsAlive(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return MachTask::IsValid(procSP->Task().TaskPort());
|
|
}
|
|
return eStateInvalid;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Process and Thread state information
|
|
//----------------------------------------------------------------------
|
|
nub_state_t DNBProcessGetState(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetState();
|
|
}
|
|
return eStateInvalid;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Process and Thread state information
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBProcessGetExitStatus(nub_process_t pid, int *status) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetExitStatus(status);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBProcessSetExitStatus(nub_process_t pid, int status) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
procSP->SetExitStatus(status);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *DNBProcessGetExitInfo(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetExitInfo();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
nub_bool_t DNBProcessSetExitInfo(nub_process_t pid, const char *info) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
procSP->SetExitInfo(info);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *DNBThreadGetName(nub_process_t pid, nub_thread_t tid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->ThreadGetName(tid);
|
|
return NULL;
|
|
}
|
|
|
|
nub_bool_t
|
|
DNBThreadGetIdentifierInfo(nub_process_t pid, nub_thread_t tid,
|
|
thread_identifier_info_data_t *ident_info) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);
|
|
return false;
|
|
}
|
|
|
|
nub_state_t DNBThreadGetState(nub_process_t pid, nub_thread_t tid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->ThreadGetState(tid);
|
|
}
|
|
return eStateInvalid;
|
|
}
|
|
|
|
const char *DNBStateAsString(nub_state_t state) {
|
|
switch (state) {
|
|
case eStateInvalid:
|
|
return "Invalid";
|
|
case eStateUnloaded:
|
|
return "Unloaded";
|
|
case eStateAttaching:
|
|
return "Attaching";
|
|
case eStateLaunching:
|
|
return "Launching";
|
|
case eStateStopped:
|
|
return "Stopped";
|
|
case eStateRunning:
|
|
return "Running";
|
|
case eStateStepping:
|
|
return "Stepping";
|
|
case eStateCrashed:
|
|
return "Crashed";
|
|
case eStateDetached:
|
|
return "Detached";
|
|
case eStateExited:
|
|
return "Exited";
|
|
case eStateSuspended:
|
|
return "Suspended";
|
|
}
|
|
return "nub_state_t ???";
|
|
}
|
|
|
|
Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread(nub_process_t pid,
|
|
nub_thread_t tid,
|
|
bool &timed_out) {
|
|
Genealogy::ThreadActivitySP thread_activity_sp;
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
thread_activity_sp = procSP->GetGenealogyInfoForThread(tid, timed_out);
|
|
return thread_activity_sp;
|
|
}
|
|
|
|
Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo(nub_process_t pid,
|
|
size_t idx) {
|
|
Genealogy::ProcessExecutableInfoSP image_info_sp;
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
image_info_sp = procSP->GetGenealogyImageInfo(idx);
|
|
}
|
|
return image_info_sp;
|
|
}
|
|
|
|
ThreadInfo::QoS DNBGetRequestedQoSForThread(nub_process_t pid, nub_thread_t tid,
|
|
nub_addr_t tsd,
|
|
uint64_t dti_qos_class_index) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetRequestedQoS(tid, tsd, dti_qos_class_index);
|
|
}
|
|
return ThreadInfo::QoS();
|
|
}
|
|
|
|
nub_addr_t DNBGetPThreadT(nub_process_t pid, nub_thread_t tid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetPThreadT(tid);
|
|
}
|
|
return INVALID_NUB_ADDRESS;
|
|
}
|
|
|
|
nub_addr_t DNBGetDispatchQueueT(nub_process_t pid, nub_thread_t tid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetDispatchQueueT(tid);
|
|
}
|
|
return INVALID_NUB_ADDRESS;
|
|
}
|
|
|
|
nub_addr_t
|
|
DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
|
|
uint64_t plo_pthread_tsd_base_address_offset,
|
|
uint64_t plo_pthread_tsd_base_offset,
|
|
uint64_t plo_pthread_tsd_entry_size) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetTSDAddressForThread(
|
|
tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
|
|
plo_pthread_tsd_entry_size);
|
|
}
|
|
return INVALID_NUB_ADDRESS;
|
|
}
|
|
|
|
JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
|
|
nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetLoadedDynamicLibrariesInfos(pid, image_list_address,
|
|
image_count);
|
|
}
|
|
return JSONGenerator::ObjectSP();
|
|
}
|
|
|
|
JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetAllLoadedLibrariesInfos(pid);
|
|
}
|
|
return JSONGenerator::ObjectSP();
|
|
}
|
|
|
|
JSONGenerator::ObjectSP
|
|
DNBGetLibrariesInfoForAddresses(nub_process_t pid,
|
|
std::vector<uint64_t> &macho_addresses) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetLibrariesInfoForAddresses(pid, macho_addresses);
|
|
}
|
|
return JSONGenerator::ObjectSP();
|
|
}
|
|
|
|
JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->GetSharedCacheInfo(pid);
|
|
}
|
|
return JSONGenerator::ObjectSP();
|
|
}
|
|
|
|
const char *DNBProcessGetExecutablePath(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->Path();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
nub_size_t DNBProcessGetArgumentCount(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->ArgumentCount();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *DNBProcessGetArgumentAtIndex(nub_process_t pid, nub_size_t idx) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->ArgumentAtIndex(idx);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Execution control
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBProcessResume(nub_process_t pid,
|
|
const DNBThreadResumeAction *actions,
|
|
size_t num_actions) {
|
|
DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
DNBThreadResumeActions thread_actions(actions, num_actions);
|
|
|
|
// Below we add a default thread plan just in case one wasn't
|
|
// provided so all threads always know what they were supposed to do
|
|
if (thread_actions.IsEmpty()) {
|
|
// No thread plans were given, so the default it to run all threads
|
|
thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
|
|
} else {
|
|
// Some thread plans were given which means anything that wasn't
|
|
// specified should remain stopped.
|
|
thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
|
|
}
|
|
return procSP->Resume(thread_actions);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBProcessHalt(nub_process_t pid) {
|
|
DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->Signal(SIGSTOP);
|
|
return false;
|
|
}
|
|
//
|
|
// nub_bool_t
|
|
// DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)
|
|
//{
|
|
// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)",
|
|
// __FUNCTION__, pid, tid, (uint32_t)step);
|
|
// MachProcessSP procSP;
|
|
// if (GetProcessSP (pid, procSP))
|
|
// {
|
|
// return procSP->Resume(tid, step, 0);
|
|
// }
|
|
// return false;
|
|
//}
|
|
//
|
|
// nub_bool_t
|
|
// DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t
|
|
// step, int signal)
|
|
//{
|
|
// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u,
|
|
// signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);
|
|
// MachProcessSP procSP;
|
|
// if (GetProcessSP (pid, procSP))
|
|
// {
|
|
// return procSP->Resume(tid, step, signal);
|
|
// }
|
|
// return false;
|
|
//}
|
|
|
|
nub_event_t DNBProcessWaitForEvents(nub_process_t pid, nub_event_t event_mask,
|
|
bool wait_for_set,
|
|
struct timespec *timeout) {
|
|
nub_event_t result = 0;
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
if (wait_for_set)
|
|
result = procSP->Events().WaitForSetEvents(event_mask, timeout);
|
|
else
|
|
result = procSP->Events().WaitForEventsToReset(event_mask, timeout);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void DNBProcessResetEvents(nub_process_t pid, nub_event_t event_mask) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
procSP->Events().ResetEvents(event_mask);
|
|
}
|
|
|
|
// Breakpoints
|
|
nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
|
|
nub_bool_t hardware) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->CreateBreakpoint(addr, size, hardware) != NULL;
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBBreakpointClear(nub_process_t pid, nub_addr_t addr) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->DisableBreakpoint(addr, true);
|
|
return false; // Failed
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Watchpoints
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBWatchpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
|
|
uint32_t watch_flags, nub_bool_t hardware) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->CreateWatchpoint(addr, size, watch_flags, hardware) != NULL;
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBWatchpointClear(nub_process_t pid, nub_addr_t addr) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->DisableWatchpoint(addr, true);
|
|
return false; // Failed
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of supported hardware watchpoints.
|
|
//----------------------------------------------------------------------
|
|
uint32_t DNBWatchpointGetNumSupportedHWP(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetNumSupportedHardwareWatchpoints();
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Read memory in the address space of process PID. This call will take
|
|
// care of setting and restoring permissions and breaking up the memory
|
|
// read into multiple chunks as required.
|
|
//
|
|
// RETURNS: number of bytes actually read
|
|
//----------------------------------------------------------------------
|
|
nub_size_t DNBProcessMemoryRead(nub_process_t pid, nub_addr_t addr,
|
|
nub_size_t size, void *buf) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->ReadMemory(addr, size, buf);
|
|
return 0;
|
|
}
|
|
|
|
uint64_t DNBProcessMemoryReadInteger(nub_process_t pid, nub_addr_t addr,
|
|
nub_size_t integer_size,
|
|
uint64_t fail_value) {
|
|
union Integers {
|
|
uint8_t u8;
|
|
uint16_t u16;
|
|
uint32_t u32;
|
|
uint64_t u64;
|
|
};
|
|
|
|
if (integer_size <= sizeof(uint64_t)) {
|
|
Integers ints;
|
|
if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) {
|
|
switch (integer_size) {
|
|
case 1:
|
|
return ints.u8;
|
|
case 2:
|
|
return ints.u16;
|
|
case 3:
|
|
return ints.u32 & 0xffffffu;
|
|
case 4:
|
|
return ints.u32;
|
|
case 5:
|
|
return ints.u32 & 0x000000ffffffffffull;
|
|
case 6:
|
|
return ints.u32 & 0x0000ffffffffffffull;
|
|
case 7:
|
|
return ints.u32 & 0x00ffffffffffffffull;
|
|
case 8:
|
|
return ints.u64;
|
|
}
|
|
}
|
|
}
|
|
return fail_value;
|
|
}
|
|
|
|
nub_addr_t DNBProcessMemoryReadPointer(nub_process_t pid, nub_addr_t addr) {
|
|
cpu_type_t cputype = DNBProcessGetCPUType(pid);
|
|
if (cputype) {
|
|
const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4;
|
|
return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
std::string DNBProcessMemoryReadCString(nub_process_t pid, nub_addr_t addr) {
|
|
std::string cstr;
|
|
char buffer[256];
|
|
const nub_size_t max_buffer_cstr_length = sizeof(buffer) - 1;
|
|
buffer[max_buffer_cstr_length] = '\0';
|
|
nub_size_t length = 0;
|
|
nub_addr_t curr_addr = addr;
|
|
do {
|
|
nub_size_t bytes_read =
|
|
DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer);
|
|
if (bytes_read == 0)
|
|
break;
|
|
length = strlen(buffer);
|
|
cstr.append(buffer, length);
|
|
curr_addr += length;
|
|
} while (length == max_buffer_cstr_length);
|
|
return cstr;
|
|
}
|
|
|
|
std::string DNBProcessMemoryReadCStringFixed(nub_process_t pid, nub_addr_t addr,
|
|
nub_size_t fixed_length) {
|
|
std::string cstr;
|
|
char buffer[fixed_length + 1];
|
|
buffer[fixed_length] = '\0';
|
|
nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer);
|
|
if (bytes_read > 0)
|
|
cstr.assign(buffer);
|
|
return cstr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Write memory to the address space of process PID. This call will take
|
|
// care of setting and restoring permissions and breaking up the memory
|
|
// write into multiple chunks as required.
|
|
//
|
|
// RETURNS: number of bytes actually written
|
|
//----------------------------------------------------------------------
|
|
nub_size_t DNBProcessMemoryWrite(nub_process_t pid, nub_addr_t addr,
|
|
nub_size_t size, const void *buf) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->WriteMemory(addr, size, buf);
|
|
return 0;
|
|
}
|
|
|
|
nub_addr_t DNBProcessMemoryAllocate(nub_process_t pid, nub_size_t size,
|
|
uint32_t permissions) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->Task().AllocateMemory(size, permissions);
|
|
return 0;
|
|
}
|
|
|
|
nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid, nub_addr_t addr) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->Task().DeallocateMemory(addr);
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Find attributes of the memory region that contains ADDR for process PID,
|
|
// if possible, and return a string describing those attributes.
|
|
//
|
|
// Returns 1 if we could find attributes for this region and OUTBUF can
|
|
// be sent to the remote debugger.
|
|
//
|
|
// Returns 0 if we couldn't find the attributes for a region of memory at
|
|
// that address and OUTBUF should not be sent.
|
|
//
|
|
// Returns -1 if this platform cannot look up information about memory regions
|
|
// or if we do not yet have a valid launched process.
|
|
//
|
|
//----------------------------------------------------------------------
|
|
int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
|
|
DNBRegionInfo *region_info) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->Task().GetMemoryRegionInfo(addr, region_info);
|
|
|
|
return -1;
|
|
}
|
|
|
|
std::string DNBProcessGetProfileData(nub_process_t pid,
|
|
DNBProfileDataScanType scanType) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->Task().GetProfileData(scanType);
|
|
|
|
return std::string("");
|
|
}
|
|
|
|
nub_bool_t DNBProcessSetEnableAsyncProfiling(nub_process_t pid,
|
|
nub_bool_t enable,
|
|
uint64_t interval_usec,
|
|
DNBProfileDataScanType scan_type) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Get the number of threads for the specified process.
|
|
//----------------------------------------------------------------------
|
|
nub_size_t DNBProcessGetNumThreads(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetNumThreads();
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Get the thread ID of the current thread.
|
|
//----------------------------------------------------------------------
|
|
nub_thread_t DNBProcessGetCurrentThread(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetCurrentThread();
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Get the mach port number of the current thread.
|
|
//----------------------------------------------------------------------
|
|
nub_thread_t DNBProcessGetCurrentThreadMachPort(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetCurrentThreadMachPort();
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Change the current thread.
|
|
//----------------------------------------------------------------------
|
|
nub_thread_t DNBProcessSetCurrentThread(nub_process_t pid, nub_thread_t tid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->SetCurrentThread(tid);
|
|
return INVALID_NUB_THREAD;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Dump a string describing a thread's stop reason to the specified file
|
|
// handle
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBThreadGetStopReason(nub_process_t pid, nub_thread_t tid,
|
|
struct DNBThreadStopInfo *stop_info) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetThreadStoppedReason(tid, stop_info);
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return string description for the specified thread.
|
|
//
|
|
// RETURNS: NULL if the thread isn't valid, else a NULL terminated C
|
|
// string from a static buffer that must be copied prior to subsequent
|
|
// calls.
|
|
//----------------------------------------------------------------------
|
|
const char *DNBThreadGetInfo(nub_process_t pid, nub_thread_t tid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetThreadInfo(tid);
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Get the thread ID given a thread index.
|
|
//----------------------------------------------------------------------
|
|
nub_thread_t DNBProcessGetThreadAtIndex(nub_process_t pid, size_t thread_idx) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetThreadAtIndex(thread_idx);
|
|
return INVALID_NUB_THREAD;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Do whatever is needed to sync the thread's register state with it's kernel
|
|
// values.
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBProcessSyncThreadState(nub_process_t pid, nub_thread_t tid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->SyncThreadState(tid);
|
|
return false;
|
|
}
|
|
|
|
nub_addr_t DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
DNBError err;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->Task().GetDYLDAllImageInfosAddress(err);
|
|
return INVALID_NUB_ADDRESS;
|
|
}
|
|
|
|
nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
procSP->SharedLibrariesUpdated();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Get the current shared library information for a process. Only return
|
|
// the shared libraries that have changed since the last shared library
|
|
// state changed event if only_changed is non-zero.
|
|
//----------------------------------------------------------------------
|
|
nub_size_t
|
|
DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,
|
|
struct DNBExecutableImageInfo **image_infos) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->CopyImageInfos(image_infos, only_changed);
|
|
|
|
// If we have no process, then return NULL for the shared library info
|
|
// and zero for shared library count
|
|
*image_infos = NULL;
|
|
return 0;
|
|
}
|
|
|
|
uint32_t DNBGetRegisterCPUType() {
|
|
return DNBArchProtocol::GetRegisterCPUType();
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// Get the register set information for a specific thread.
|
|
//----------------------------------------------------------------------
|
|
const DNBRegisterSetInfo *DNBGetRegisterSetInfo(nub_size_t *num_reg_sets) {
|
|
return DNBArchProtocol::GetRegisterSetInfo(num_reg_sets);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Read a register value by register set and register index.
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBThreadGetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
|
|
uint32_t set, uint32_t reg,
|
|
DNBRegisterValue *value) {
|
|
MachProcessSP procSP;
|
|
::bzero(value, sizeof(DNBRegisterValue));
|
|
if (GetProcessSP(pid, procSP)) {
|
|
if (tid != INVALID_NUB_THREAD)
|
|
return procSP->GetRegisterValue(tid, set, reg, value);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_bool_t DNBThreadSetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
|
|
uint32_t set, uint32_t reg,
|
|
const DNBRegisterValue *value) {
|
|
if (tid != INVALID_NUB_THREAD) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->SetRegisterValue(tid, set, reg, value);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_size_t DNBThreadGetRegisterContext(nub_process_t pid, nub_thread_t tid,
|
|
void *buf, size_t buf_len) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
if (tid != INVALID_NUB_THREAD)
|
|
return procSP->GetThreadList().GetRegisterContext(tid, buf, buf_len);
|
|
}
|
|
::bzero(buf, buf_len);
|
|
return 0;
|
|
}
|
|
|
|
nub_size_t DNBThreadSetRegisterContext(nub_process_t pid, nub_thread_t tid,
|
|
const void *buf, size_t buf_len) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
if (tid != INVALID_NUB_THREAD)
|
|
return procSP->GetThreadList().SetRegisterContext(tid, buf, buf_len);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32_t DNBThreadSaveRegisterState(nub_process_t pid, nub_thread_t tid) {
|
|
if (tid != INVALID_NUB_THREAD) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetThreadList().SaveRegisterState(tid);
|
|
}
|
|
return 0;
|
|
}
|
|
nub_bool_t DNBThreadRestoreRegisterState(nub_process_t pid, nub_thread_t tid,
|
|
uint32_t save_id) {
|
|
if (tid != INVALID_NUB_THREAD) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetThreadList().RestoreRegisterState(tid, save_id);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Read a register value by name.
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBThreadGetRegisterValueByName(nub_process_t pid, nub_thread_t tid,
|
|
uint32_t reg_set,
|
|
const char *reg_name,
|
|
DNBRegisterValue *value) {
|
|
MachProcessSP procSP;
|
|
::bzero(value, sizeof(DNBRegisterValue));
|
|
if (GetProcessSP(pid, procSP)) {
|
|
const struct DNBRegisterSetInfo *set_info;
|
|
nub_size_t num_reg_sets = 0;
|
|
set_info = DNBGetRegisterSetInfo(&num_reg_sets);
|
|
if (set_info) {
|
|
uint32_t set = reg_set;
|
|
uint32_t reg;
|
|
if (set == REGISTER_SET_ALL) {
|
|
for (set = 1; set < num_reg_sets; ++set) {
|
|
for (reg = 0; reg < set_info[set].num_registers; ++reg) {
|
|
if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
|
|
return procSP->GetRegisterValue(tid, set, reg, value);
|
|
}
|
|
}
|
|
} else {
|
|
for (reg = 0; reg < set_info[set].num_registers; ++reg) {
|
|
if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
|
|
return procSP->GetRegisterValue(tid, set, reg, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Read a register set and register number from the register name.
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBGetRegisterInfoByName(const char *reg_name,
|
|
DNBRegisterInfo *info) {
|
|
const struct DNBRegisterSetInfo *set_info;
|
|
nub_size_t num_reg_sets = 0;
|
|
set_info = DNBGetRegisterSetInfo(&num_reg_sets);
|
|
if (set_info) {
|
|
uint32_t set, reg;
|
|
for (set = 1; set < num_reg_sets; ++set) {
|
|
for (reg = 0; reg < set_info[set].num_registers; ++reg) {
|
|
if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) {
|
|
*info = set_info[set].registers[reg];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (set = 1; set < num_reg_sets; ++set) {
|
|
uint32_t reg;
|
|
for (reg = 0; reg < set_info[set].num_registers; ++reg) {
|
|
if (set_info[set].registers[reg].alt == NULL)
|
|
continue;
|
|
|
|
if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0) {
|
|
*info = set_info[set].registers[reg];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
::bzero(info, sizeof(DNBRegisterInfo));
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set the name to address callback function that this nub can use
|
|
// for any name to address lookups that are needed.
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,
|
|
DNBCallbackNameToAddress callback,
|
|
void *baton) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
procSP->SetNameToAddressCallback(callback, baton);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set the name to address callback function that this nub can use
|
|
// for any name to address lookups that are needed.
|
|
//----------------------------------------------------------------------
|
|
nub_bool_t DNBProcessSetSharedLibraryInfoCallback(
|
|
nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback,
|
|
void *baton) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
procSP->SetSharedLibraryInfoCallback(callback, baton);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nub_addr_t DNBProcessLookupAddress(nub_process_t pid, const char *name,
|
|
const char *shlib) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP)) {
|
|
return procSP->LookupSymbol(name, shlib);
|
|
}
|
|
return INVALID_NUB_ADDRESS;
|
|
}
|
|
|
|
nub_size_t DNBProcessGetAvailableSTDOUT(nub_process_t pid, char *buf,
|
|
nub_size_t buf_size) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetAvailableSTDOUT(buf, buf_size);
|
|
return 0;
|
|
}
|
|
|
|
nub_size_t DNBProcessGetAvailableSTDERR(nub_process_t pid, char *buf,
|
|
nub_size_t buf_size) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetAvailableSTDERR(buf, buf_size);
|
|
return 0;
|
|
}
|
|
|
|
nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,
|
|
nub_size_t buf_size) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetAsyncProfileData(buf, buf_size);
|
|
return 0;
|
|
}
|
|
|
|
DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid) {
|
|
return DarwinLogCollector::GetEventsForProcess(pid);
|
|
}
|
|
|
|
nub_size_t DNBProcessGetStopCount(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->StopCount();
|
|
return 0;
|
|
}
|
|
|
|
uint32_t DNBProcessGetCPUType(nub_process_t pid) {
|
|
MachProcessSP procSP;
|
|
if (GetProcessSP(pid, procSP))
|
|
return procSP->GetCPUType();
|
|
return 0;
|
|
}
|
|
|
|
nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path,
|
|
size_t resolved_path_size) {
|
|
if (path == NULL || path[0] == '\0')
|
|
return false;
|
|
|
|
char max_path[PATH_MAX];
|
|
std::string result;
|
|
CFString::GlobPath(path, result);
|
|
|
|
if (result.empty())
|
|
result = path;
|
|
|
|
struct stat path_stat;
|
|
if (::stat(path, &path_stat) == 0) {
|
|
if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
|
|
CFBundle bundle(path);
|
|
CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
|
|
if (url.get()) {
|
|
if (::CFURLGetFileSystemRepresentation(
|
|
url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (realpath(path, max_path)) {
|
|
// Found the path relatively...
|
|
::strncpy(resolved_path, max_path, resolved_path_size);
|
|
return strlen(resolved_path) + 1 < resolved_path_size;
|
|
} else {
|
|
// Not a relative path, check the PATH environment variable if the
|
|
const char *PATH = getenv("PATH");
|
|
if (PATH) {
|
|
const char *curr_path_start = PATH;
|
|
const char *curr_path_end;
|
|
while (curr_path_start && *curr_path_start) {
|
|
curr_path_end = strchr(curr_path_start, ':');
|
|
if (curr_path_end == NULL) {
|
|
result.assign(curr_path_start);
|
|
curr_path_start = NULL;
|
|
} else if (curr_path_end > curr_path_start) {
|
|
size_t len = curr_path_end - curr_path_start;
|
|
result.assign(curr_path_start, len);
|
|
curr_path_start += len + 1;
|
|
} else
|
|
break;
|
|
|
|
result += '/';
|
|
result += path;
|
|
struct stat s;
|
|
if (stat(result.c_str(), &s) == 0) {
|
|
::strncpy(resolved_path, result.c_str(), resolved_path_size);
|
|
return result.size() + 1 < resolved_path_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch) {
|
|
return MachProcess::GetOSVersionNumbers(major, minor, patch);
|
|
}
|
|
|
|
void DNBInitialize() {
|
|
DNBLogThreadedIf(LOG_PROCESS, "DNBInitialize ()");
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
DNBArchImplI386::Initialize();
|
|
DNBArchImplX86_64::Initialize();
|
|
#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
|
|
DNBArchMachARM::Initialize();
|
|
DNBArchMachARM64::Initialize();
|
|
#endif
|
|
}
|
|
|
|
void DNBTerminate() {}
|
|
|
|
nub_bool_t DNBSetArchitecture(const char *arch) {
|
|
if (arch && arch[0]) {
|
|
if (strcasecmp(arch, "i386") == 0)
|
|
return DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
|
|
else if ((strcasecmp(arch, "x86_64") == 0) ||
|
|
(strcasecmp(arch, "x86_64h") == 0))
|
|
return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64);
|
|
else if (strstr(arch, "arm64") == arch || strstr(arch, "armv8") == arch ||
|
|
strstr(arch, "aarch64") == arch)
|
|
return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
|
|
else if (strstr(arch, "arm") == arch)
|
|
return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
|
|
}
|
|
return false;
|
|
}
|