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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  Created by Greg Clayton on 3/23/07.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "DNB.h"
14 #include <inttypes.h>
15 #include <libproc.h>
16 #include <map>
17 #include <signal.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/resource.h>
21 #include <sys/stat.h>
22 #include <sys/sysctl.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 #include <vector>
27 
28 #if defined(__APPLE__)
29 #include <pthread.h>
30 #include <sched.h>
31 #endif
32 
33 #define TRY_KQUEUE 1
34 
35 #ifdef TRY_KQUEUE
36 #include <sys/event.h>
37 #include <sys/time.h>
38 #ifdef NOTE_EXIT_DETAIL
39 #define USE_KQUEUE
40 #endif
41 #endif
42 
43 #include "CFBundle.h"
44 #include "CFString.h"
45 #include "DNBDataRef.h"
46 #include "DNBLog.h"
47 #include "DNBThreadResumeActions.h"
48 #include "DNBTimer.h"
49 #include "MacOSX/DarwinLog/DarwinLogCollector.h"
50 #include "MacOSX/Genealogy.h"
51 #include "MacOSX/MachProcess.h"
52 #include "MacOSX/MachTask.h"
53 #include "MacOSX/ThreadInfo.h"
54 
55 typedef std::shared_ptr<MachProcess> MachProcessSP;
56 typedef std::map<nub_process_t, MachProcessSP> ProcessMap;
57 typedef ProcessMap::iterator ProcessMapIter;
58 typedef ProcessMap::const_iterator ProcessMapConstIter;
59 
60 size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos);
61 static size_t
62 GetAllInfosMatchingName(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 a
68 // newly created process map if CAN_CREATE is non-zero.
GetProcessMap(bool can_create)69 static 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 pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER;
74     PTHREAD_MUTEX_LOCKER(locker, &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't
85 // allocate a ProcessMap.
AddProcessToMap(nub_process_t pid,MachProcessSP & procSP)86 static 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_t
99 // 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 a
112 // MachProcess object.
GetProcessSP(nub_process_t pid,MachProcessSP & procSP)113 static 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_KQUEUE
kqueue_thread(void * arg)127 void *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 #endif
141 #endif
142 
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 
spawn_kqueue_thread(pid_t pid)218 static 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 successful
245   if (ret == 0) {
246     ::pthread_detach(thread);
247     return true;
248   }
249   return false;
250 }
251 #endif // #if USE_KQUEUE
252 
waitpid_thread(void * arg)253 static 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 #endif
268 #endif
269 
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 we
296   // 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 }
spawn_waitpid_thread(pid_t pid)304 static bool spawn_waitpid_thread(pid_t pid) {
305 #ifdef USE_KQUEUE
306   bool success = spawn_kqueue_thread(pid);
307   if (success)
308     return true;
309 #endif
310 
311   pthread_t thread;
312   int ret =
313       ::pthread_create(&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);
314   // pthread_create returns 0 if successful
315   if (ret == 0) {
316     ::pthread_detach(thread);
317     return true;
318   }
319   return false;
320 }
321 
DNBProcessLaunch(const char * path,char const * argv[],const char * envp[],const char * working_directory,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)322 nub_process_t DNBProcessLaunch(
323     const char *path, char const *argv[], const char *envp[],
324     const char *working_directory, // NULL => don't change, non-NULL => set
325                                    // working directory for inferior to this
326     const char *stdin_path, const char *stdout_path, const char *stderr_path,
327     bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr,
328     const char *event_data, char *err_str, size_t err_len) {
329   DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv = %p, envp = %p, "
330                                 "working_dir=%s, stdin=%s, stdout=%s, "
331                                 "stderr=%s, no-stdio=%i, launch_flavor = %u, "
332                                 "disable_aslr = %d, err = %p, err_len = "
333                                 "%llu) called...",
334                    __FUNCTION__, path, static_cast<void *>(argv),
335                    static_cast<void *>(envp), working_directory, stdin_path,
336                    stdout_path, stderr_path, no_stdio, launch_flavor,
337                    disable_aslr, static_cast<void *>(err_str),
338                    static_cast<uint64_t>(err_len));
339 
340   if (err_str && err_len > 0)
341     err_str[0] = '\0';
342   struct stat path_stat;
343   if (::stat(path, &path_stat) == -1) {
344     char stat_error[256];
345     ::strerror_r(errno, stat_error, sizeof(stat_error));
346     snprintf(err_str, err_len, "%s (%s)", stat_error, path);
347     return INVALID_NUB_PROCESS;
348   }
349 
350   MachProcessSP processSP(new MachProcess);
351   if (processSP.get()) {
352     DNBError launch_err;
353     pid_t pid = processSP->LaunchForDebug(path, argv, envp, working_directory,
354                                           stdin_path, stdout_path, stderr_path,
355                                           no_stdio, launch_flavor, disable_aslr,
356                                           event_data, launch_err);
357     if (err_str) {
358       *err_str = '\0';
359       if (launch_err.Fail()) {
360         const char *launch_err_str = launch_err.AsString();
361         if (launch_err_str) {
362           strlcpy(err_str, launch_err_str, err_len - 1);
363           err_str[err_len - 1] =
364               '\0'; // Make sure the error string is terminated
365         }
366       }
367     }
368 
369     DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid);
370 
371     if (pid != INVALID_NUB_PROCESS) {
372       // Spawn a thread to reap our child inferior process...
373       spawn_waitpid_thread(pid);
374 
375       if (processSP->Task().TaskPortForProcessID(launch_err) == TASK_NULL) {
376         // We failed to get the task for our process ID which is bad.
377         // Kill our process otherwise it will be stopped at the entry
378         // point and get reparented to someone else and never go away.
379         DNBLog("Could not get task port for process, sending SIGKILL and "
380                "exiting.");
381         kill(SIGKILL, pid);
382 
383         if (err_str && err_len > 0) {
384           if (launch_err.AsString()) {
385             ::snprintf(err_str, err_len,
386                        "failed to get the task for process %i (%s)", pid,
387                        launch_err.AsString());
388           } else {
389             ::snprintf(err_str, err_len,
390                        "failed to get the task for process %i", pid);
391           }
392         }
393       } else {
394         bool res = AddProcessToMap(pid, processSP);
395         UNUSED_IF_ASSERT_DISABLED(res);
396         assert(res && "Couldn't add process to map!");
397         return pid;
398       }
399     }
400   }
401   return INVALID_NUB_PROCESS;
402 }
403 
404 // If there is one process with a given name, return the pid for that process.
DNBProcessGetPIDByName(const char * name)405 nub_process_t DNBProcessGetPIDByName(const char *name) {
406   std::vector<struct kinfo_proc> matching_proc_infos;
407   size_t num_matching_proc_infos =
408       GetAllInfosMatchingName(name, matching_proc_infos);
409   if (num_matching_proc_infos == 1) {
410     return matching_proc_infos[0].kp_proc.p_pid;
411   }
412   return INVALID_NUB_PROCESS;
413 }
414 
DNBProcessAttachByName(const char * name,struct timespec * timeout,char * err_str,size_t err_len)415 nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout,
416                                      char *err_str, size_t err_len) {
417   if (err_str && err_len > 0)
418     err_str[0] = '\0';
419   std::vector<struct kinfo_proc> matching_proc_infos;
420   size_t num_matching_proc_infos =
421       GetAllInfosMatchingName(name, matching_proc_infos);
422   if (num_matching_proc_infos == 0) {
423     DNBLogError("error: no processes match '%s'\n", name);
424     return INVALID_NUB_PROCESS;
425   } else if (num_matching_proc_infos > 1) {
426     DNBLogError("error: %llu processes match '%s':\n",
427                 (uint64_t)num_matching_proc_infos, name);
428     size_t i;
429     for (i = 0; i < num_matching_proc_infos; ++i)
430       DNBLogError("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid,
431                   matching_proc_infos[i].kp_proc.p_comm);
432     return INVALID_NUB_PROCESS;
433   }
434 
435   return DNBProcessAttach(matching_proc_infos[0].kp_proc.p_pid, timeout,
436                           err_str, err_len);
437 }
438 
DNBProcessAttach(nub_process_t attach_pid,struct timespec * timeout,char * err_str,size_t err_len)439 nub_process_t DNBProcessAttach(nub_process_t attach_pid,
440                                struct timespec *timeout, char *err_str,
441                                size_t err_len) {
442   if (err_str && err_len > 0)
443     err_str[0] = '\0';
444 
445   pid_t pid = INVALID_NUB_PROCESS;
446   MachProcessSP processSP(new MachProcess);
447   if (processSP.get()) {
448     DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...",
449                      attach_pid);
450     pid = processSP->AttachForDebug(attach_pid, err_str, err_len);
451 
452     if (pid != INVALID_NUB_PROCESS) {
453       bool res = AddProcessToMap(pid, processSP);
454       UNUSED_IF_ASSERT_DISABLED(res);
455       assert(res && "Couldn't add process to map!");
456       spawn_waitpid_thread(pid);
457     }
458   }
459 
460   while (pid != INVALID_NUB_PROCESS) {
461     // Wait for process to start up and hit entry point
462     DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
463                                   "eEventProcessRunningStateChanged | "
464                                   "eEventProcessStoppedStateChanged, true, "
465                                   "INFINITE)...",
466                      __FUNCTION__, pid);
467     nub_event_t set_events =
468         DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |
469                                          eEventProcessStoppedStateChanged,
470                                 true, timeout);
471 
472     DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
473                                   "eEventProcessRunningStateChanged | "
474                                   "eEventProcessStoppedStateChanged, true, "
475                                   "INFINITE) => 0x%8.8x",
476                      __FUNCTION__, pid, set_events);
477 
478     if (set_events == 0) {
479       if (err_str && err_len > 0)
480         snprintf(err_str, err_len, "operation timed out");
481       pid = INVALID_NUB_PROCESS;
482     } else {
483       if (set_events & (eEventProcessRunningStateChanged |
484                         eEventProcessStoppedStateChanged)) {
485         nub_state_t pid_state = DNBProcessGetState(pid);
486         DNBLogThreadedIf(
487             LOG_PROCESS,
488             "%s process %4.4x state changed (eEventProcessStateChanged): %s",
489             __FUNCTION__, pid, DNBStateAsString(pid_state));
490 
491         switch (pid_state) {
492         case eStateInvalid:
493         case eStateUnloaded:
494         case eStateAttaching:
495         case eStateLaunching:
496         case eStateSuspended:
497           break; // Ignore
498 
499         case eStateRunning:
500         case eStateStepping:
501           // Still waiting to stop at entry point...
502           break;
503 
504         case eStateStopped:
505         case eStateCrashed:
506           return pid;
507 
508         case eStateDetached:
509         case eStateExited:
510           if (err_str && err_len > 0)
511             snprintf(err_str, err_len, "process exited");
512           return INVALID_NUB_PROCESS;
513         }
514       }
515 
516       DNBProcessResetEvents(pid, set_events);
517     }
518   }
519 
520   return INVALID_NUB_PROCESS;
521 }
522 
GetAllInfos(std::vector<struct kinfo_proc> & proc_infos)523 size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos) {
524   size_t size = 0;
525   int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
526   u_int namelen = sizeof(name) / sizeof(int);
527   int err;
528 
529   // Try to find out how many processes are around so we can
530   // size the buffer appropriately.  sysctl's man page specifically suggests
531   // this approach, and says it returns a bit larger size than needed to
532   // handle any new processes created between then and now.
533 
534   err = ::sysctl(name, namelen, NULL, &size, NULL, 0);
535 
536   if ((err < 0) && (err != ENOMEM)) {
537     proc_infos.clear();
538     perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");
539     return 0;
540   }
541 
542   // Increase the size of the buffer by a few processes in case more have
543   // been spawned
544   proc_infos.resize(size / sizeof(struct kinfo_proc));
545   size = proc_infos.size() *
546          sizeof(struct kinfo_proc); // Make sure we don't exceed our resize...
547   err = ::sysctl(name, namelen, &proc_infos[0], &size, NULL, 0);
548   if (err < 0) {
549     proc_infos.clear();
550     return 0;
551   }
552 
553   // Trim down our array to fit what we actually got back
554   proc_infos.resize(size / sizeof(struct kinfo_proc));
555   return proc_infos.size();
556 }
557 
558 static size_t
GetAllInfosMatchingName(const char * full_process_name,std::vector<struct kinfo_proc> & matching_proc_infos)559 GetAllInfosMatchingName(const char *full_process_name,
560                         std::vector<struct kinfo_proc> &matching_proc_infos) {
561 
562   matching_proc_infos.clear();
563   if (full_process_name && full_process_name[0]) {
564     // We only get the process name, not the full path, from the proc_info.  So
565     // just take the
566     // base name of the process name...
567     const char *process_name;
568     process_name = strrchr(full_process_name, '/');
569     if (process_name == NULL)
570       process_name = full_process_name;
571     else
572       process_name++;
573 
574     const size_t process_name_len = strlen(process_name);
575     std::vector<struct kinfo_proc> proc_infos;
576     const size_t num_proc_infos = GetAllInfos(proc_infos);
577     if (num_proc_infos > 0) {
578       uint32_t i;
579       for (i = 0; i < num_proc_infos; i++) {
580         // Skip zombie processes and processes with unset status
581         if (proc_infos[i].kp_proc.p_stat == 0 ||
582             proc_infos[i].kp_proc.p_stat == SZOMB)
583           continue;
584 
585         // Check for process by name. We only check the first MAXCOMLEN
586         // chars as that is all that kp_proc.p_comm holds.
587 
588         if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm,
589                           MAXCOMLEN) == 0) {
590           if (process_name_len > MAXCOMLEN) {
591             // We found a matching process name whose first MAXCOMLEN
592             // characters match, but there is more to the name than
593             // this. We need to get the full process name.  Use proc_pidpath,
594             // which will get
595             // us the full path to the executed process.
596 
597             char proc_path_buf[PATH_MAX];
598 
599             int return_val = proc_pidpath(proc_infos[i].kp_proc.p_pid,
600                                           proc_path_buf, PATH_MAX);
601             if (return_val > 0) {
602               // Okay, now search backwards from that to see if there is a
603               // slash in the name.  Note, even though we got all the args we
604               // don't care
605               // because the list data is just a bunch of concatenated null
606               // terminated strings
607               // so strrchr will start from the end of argv0.
608 
609               const char *argv_basename = strrchr(proc_path_buf, '/');
610               if (argv_basename) {
611                 // Skip the '/'
612                 ++argv_basename;
613               } else {
614                 // We didn't find a directory delimiter in the process argv[0],
615                 // just use what was in there
616                 argv_basename = proc_path_buf;
617               }
618 
619               if (argv_basename) {
620                 if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0) {
621                   matching_proc_infos.push_back(proc_infos[i]);
622                 }
623               }
624             }
625           } else {
626             // We found a matching process, add it to our list
627             matching_proc_infos.push_back(proc_infos[i]);
628           }
629         }
630       }
631     }
632   }
633   // return the newly added matches.
634   return matching_proc_infos.size();
635 }
636 
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)637 nub_process_t DNBProcessAttachWait(
638     const char *waitfor_process_name, nub_launch_flavor_t launch_flavor,
639     bool ignore_existing, struct timespec *timeout_abstime,
640     useconds_t waitfor_interval, char *err_str, size_t err_len,
641     DNBShouldCancelCallback should_cancel_callback, void *callback_data) {
642   DNBError prepare_error;
643   std::vector<struct kinfo_proc> exclude_proc_infos;
644   size_t num_exclude_proc_infos;
645 
646   // If the PrepareForAttach returns a valid token, use  MachProcess to check
647   // for the process, otherwise scan the process table.
648 
649   const void *attach_token = MachProcess::PrepareForAttach(
650       waitfor_process_name, launch_flavor, true, prepare_error);
651 
652   if (prepare_error.Fail()) {
653     DNBLogError("Error in PrepareForAttach: %s", prepare_error.AsString());
654     return INVALID_NUB_PROCESS;
655   }
656 
657   if (attach_token == NULL) {
658     if (ignore_existing)
659       num_exclude_proc_infos =
660           GetAllInfosMatchingName(waitfor_process_name, exclude_proc_infos);
661     else
662       num_exclude_proc_infos = 0;
663   }
664 
665   DNBLogThreadedIf(LOG_PROCESS, "Waiting for '%s' to appear...\n",
666                    waitfor_process_name);
667 
668   // Loop and try to find the process by name
669   nub_process_t waitfor_pid = INVALID_NUB_PROCESS;
670 
671   while (waitfor_pid == INVALID_NUB_PROCESS) {
672     if (attach_token != NULL) {
673       nub_process_t pid;
674       pid = MachProcess::CheckForProcess(attach_token, launch_flavor);
675       if (pid != INVALID_NUB_PROCESS) {
676         waitfor_pid = pid;
677         break;
678       }
679     } else {
680 
681       // Get the current process list, and check for matches that
682       // aren't in our original list. If anyone wants to attach
683       // to an existing process by name, they should do it with
684       // --attach=PROCNAME. Else we will wait for the first matching
685       // process that wasn't in our exclusion list.
686       std::vector<struct kinfo_proc> proc_infos;
687       const size_t num_proc_infos =
688           GetAllInfosMatchingName(waitfor_process_name, proc_infos);
689       for (size_t i = 0; i < num_proc_infos; i++) {
690         nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;
691         for (size_t j = 0; j < num_exclude_proc_infos; j++) {
692           if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid) {
693             // This process was in our exclusion list, don't use it.
694             curr_pid = INVALID_NUB_PROCESS;
695             break;
696           }
697         }
698 
699         // If we didn't find CURR_PID in our exclusion list, then use it.
700         if (curr_pid != INVALID_NUB_PROCESS) {
701           // We found our process!
702           waitfor_pid = curr_pid;
703           break;
704         }
705       }
706     }
707 
708     // If we haven't found our process yet, check for a timeout
709     // and then sleep for a bit until we poll again.
710     if (waitfor_pid == INVALID_NUB_PROCESS) {
711       if (timeout_abstime != NULL) {
712         // Check to see if we have a waitfor-duration option that
713         // has timed out?
714         if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime)) {
715           if (err_str && err_len > 0)
716             snprintf(err_str, err_len, "operation timed out");
717           DNBLogError("error: waiting for process '%s' timed out.\n",
718                       waitfor_process_name);
719           return INVALID_NUB_PROCESS;
720         }
721       }
722 
723       // Call the should cancel callback as well...
724 
725       if (should_cancel_callback != NULL &&
726           should_cancel_callback(callback_data)) {
727         DNBLogThreadedIf(
728             LOG_PROCESS,
729             "DNBProcessAttachWait cancelled by should_cancel callback.");
730         waitfor_pid = INVALID_NUB_PROCESS;
731         break;
732       }
733 
734       ::usleep(waitfor_interval); // Sleep for WAITFOR_INTERVAL, then poll again
735     }
736   }
737 
738   if (waitfor_pid != INVALID_NUB_PROCESS) {
739     DNBLogThreadedIf(LOG_PROCESS, "Attaching to %s with pid %i...\n",
740                      waitfor_process_name, waitfor_pid);
741     waitfor_pid =
742         DNBProcessAttach(waitfor_pid, timeout_abstime, err_str, err_len);
743   }
744 
745   bool success = waitfor_pid != INVALID_NUB_PROCESS;
746   MachProcess::CleanupAfterAttach(attach_token, launch_flavor, success,
747                                   prepare_error);
748 
749   return waitfor_pid;
750 }
751 
DNBProcessDetach(nub_process_t pid)752 nub_bool_t DNBProcessDetach(nub_process_t pid) {
753   MachProcessSP procSP;
754   if (GetProcessSP(pid, procSP)) {
755     const bool remove = true;
756     DNBLogThreaded(
757         "Disabling breakpoints and watchpoints, and detaching from %d.", pid);
758     procSP->DisableAllBreakpoints(remove);
759     procSP->DisableAllWatchpoints(remove);
760     return procSP->Detach();
761   }
762   return false;
763 }
764 
DNBProcessKill(nub_process_t pid)765 nub_bool_t DNBProcessKill(nub_process_t pid) {
766   MachProcessSP procSP;
767   if (GetProcessSP(pid, procSP)) {
768     return procSP->Kill();
769   }
770   return false;
771 }
772 
DNBProcessSignal(nub_process_t pid,int signal)773 nub_bool_t DNBProcessSignal(nub_process_t pid, int signal) {
774   MachProcessSP procSP;
775   if (GetProcessSP(pid, procSP)) {
776     return procSP->Signal(signal);
777   }
778   return false;
779 }
780 
DNBProcessInterrupt(nub_process_t pid)781 nub_bool_t DNBProcessInterrupt(nub_process_t pid) {
782   MachProcessSP procSP;
783   if (GetProcessSP(pid, procSP))
784     return procSP->Interrupt();
785   return false;
786 }
787 
DNBProcessSendEvent(nub_process_t pid,const char * event)788 nub_bool_t DNBProcessSendEvent(nub_process_t pid, const char *event) {
789   MachProcessSP procSP;
790   if (GetProcessSP(pid, procSP)) {
791     // FIXME: Do something with the error...
792     DNBError send_error;
793     return procSP->SendEvent(event, send_error);
794   }
795   return false;
796 }
797 
DNBProcessIsAlive(nub_process_t pid)798 nub_bool_t DNBProcessIsAlive(nub_process_t pid) {
799   MachProcessSP procSP;
800   if (GetProcessSP(pid, procSP)) {
801     return MachTask::IsValid(procSP->Task().TaskPort());
802   }
803   return eStateInvalid;
804 }
805 
806 // Process and Thread state information
DNBProcessGetState(nub_process_t pid)807 nub_state_t DNBProcessGetState(nub_process_t pid) {
808   MachProcessSP procSP;
809   if (GetProcessSP(pid, procSP)) {
810     return procSP->GetState();
811   }
812   return eStateInvalid;
813 }
814 
815 // Process and Thread state information
DNBProcessGetExitStatus(nub_process_t pid,int * status)816 nub_bool_t DNBProcessGetExitStatus(nub_process_t pid, int *status) {
817   MachProcessSP procSP;
818   if (GetProcessSP(pid, procSP)) {
819     return procSP->GetExitStatus(status);
820   }
821   return false;
822 }
823 
DNBProcessSetExitStatus(nub_process_t pid,int status)824 nub_bool_t DNBProcessSetExitStatus(nub_process_t pid, int status) {
825   MachProcessSP procSP;
826   if (GetProcessSP(pid, procSP)) {
827     procSP->SetExitStatus(status);
828     return true;
829   }
830   return false;
831 }
832 
DNBProcessGetExitInfo(nub_process_t pid)833 const char *DNBProcessGetExitInfo(nub_process_t pid) {
834   MachProcessSP procSP;
835   if (GetProcessSP(pid, procSP)) {
836     return procSP->GetExitInfo();
837   }
838   return NULL;
839 }
840 
DNBProcessSetExitInfo(nub_process_t pid,const char * info)841 nub_bool_t DNBProcessSetExitInfo(nub_process_t pid, const char *info) {
842   MachProcessSP procSP;
843   if (GetProcessSP(pid, procSP)) {
844     procSP->SetExitInfo(info);
845     return true;
846   }
847   return false;
848 }
849 
DNBThreadGetName(nub_process_t pid,nub_thread_t tid)850 const char *DNBThreadGetName(nub_process_t pid, nub_thread_t tid) {
851   MachProcessSP procSP;
852   if (GetProcessSP(pid, procSP))
853     return procSP->ThreadGetName(tid);
854   return NULL;
855 }
856 
857 nub_bool_t
DNBThreadGetIdentifierInfo(nub_process_t pid,nub_thread_t tid,thread_identifier_info_data_t * ident_info)858 DNBThreadGetIdentifierInfo(nub_process_t pid, nub_thread_t tid,
859                            thread_identifier_info_data_t *ident_info) {
860   MachProcessSP procSP;
861   if (GetProcessSP(pid, procSP))
862     return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);
863   return false;
864 }
865 
DNBThreadGetState(nub_process_t pid,nub_thread_t tid)866 nub_state_t DNBThreadGetState(nub_process_t pid, nub_thread_t tid) {
867   MachProcessSP procSP;
868   if (GetProcessSP(pid, procSP)) {
869     return procSP->ThreadGetState(tid);
870   }
871   return eStateInvalid;
872 }
873 
DNBStateAsString(nub_state_t state)874 const char *DNBStateAsString(nub_state_t state) {
875   switch (state) {
876   case eStateInvalid:
877     return "Invalid";
878   case eStateUnloaded:
879     return "Unloaded";
880   case eStateAttaching:
881     return "Attaching";
882   case eStateLaunching:
883     return "Launching";
884   case eStateStopped:
885     return "Stopped";
886   case eStateRunning:
887     return "Running";
888   case eStateStepping:
889     return "Stepping";
890   case eStateCrashed:
891     return "Crashed";
892   case eStateDetached:
893     return "Detached";
894   case eStateExited:
895     return "Exited";
896   case eStateSuspended:
897     return "Suspended";
898   }
899   return "nub_state_t ???";
900 }
901 
DNBGetGenealogyInfoForThread(nub_process_t pid,nub_thread_t tid,bool & timed_out)902 Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread(nub_process_t pid,
903                                                          nub_thread_t tid,
904                                                          bool &timed_out) {
905   Genealogy::ThreadActivitySP thread_activity_sp;
906   MachProcessSP procSP;
907   if (GetProcessSP(pid, procSP))
908     thread_activity_sp = procSP->GetGenealogyInfoForThread(tid, timed_out);
909   return thread_activity_sp;
910 }
911 
DNBGetGenealogyImageInfo(nub_process_t pid,size_t idx)912 Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo(nub_process_t pid,
913                                                             size_t idx) {
914   Genealogy::ProcessExecutableInfoSP image_info_sp;
915   MachProcessSP procSP;
916   if (GetProcessSP(pid, procSP)) {
917     image_info_sp = procSP->GetGenealogyImageInfo(idx);
918   }
919   return image_info_sp;
920 }
921 
DNBGetRequestedQoSForThread(nub_process_t pid,nub_thread_t tid,nub_addr_t tsd,uint64_t dti_qos_class_index)922 ThreadInfo::QoS DNBGetRequestedQoSForThread(nub_process_t pid, nub_thread_t tid,
923                                             nub_addr_t tsd,
924                                             uint64_t dti_qos_class_index) {
925   MachProcessSP procSP;
926   if (GetProcessSP(pid, procSP)) {
927     return procSP->GetRequestedQoS(tid, tsd, dti_qos_class_index);
928   }
929   return ThreadInfo::QoS();
930 }
931 
DNBGetPThreadT(nub_process_t pid,nub_thread_t tid)932 nub_addr_t DNBGetPThreadT(nub_process_t pid, nub_thread_t tid) {
933   MachProcessSP procSP;
934   if (GetProcessSP(pid, procSP)) {
935     return procSP->GetPThreadT(tid);
936   }
937   return INVALID_NUB_ADDRESS;
938 }
939 
DNBGetDispatchQueueT(nub_process_t pid,nub_thread_t tid)940 nub_addr_t DNBGetDispatchQueueT(nub_process_t pid, nub_thread_t tid) {
941   MachProcessSP procSP;
942   if (GetProcessSP(pid, procSP)) {
943     return procSP->GetDispatchQueueT(tid);
944   }
945   return INVALID_NUB_ADDRESS;
946 }
947 
948 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)949 DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
950                           uint64_t plo_pthread_tsd_base_address_offset,
951                           uint64_t plo_pthread_tsd_base_offset,
952                           uint64_t plo_pthread_tsd_entry_size) {
953   MachProcessSP procSP;
954   if (GetProcessSP(pid, procSP)) {
955     return procSP->GetTSDAddressForThread(
956         tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
957         plo_pthread_tsd_entry_size);
958   }
959   return INVALID_NUB_ADDRESS;
960 }
961 
DNBGetLoadedDynamicLibrariesInfos(nub_process_t pid,nub_addr_t image_list_address,nub_addr_t image_count)962 JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
963     nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
964   MachProcessSP procSP;
965   if (GetProcessSP(pid, procSP)) {
966     return procSP->GetLoadedDynamicLibrariesInfos(pid, image_list_address,
967                                                   image_count);
968   }
969   return JSONGenerator::ObjectSP();
970 }
971 
DNBGetAllLoadedLibrariesInfos(nub_process_t pid)972 JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid) {
973   MachProcessSP procSP;
974   if (GetProcessSP(pid, procSP)) {
975     return procSP->GetAllLoadedLibrariesInfos(pid);
976   }
977   return JSONGenerator::ObjectSP();
978 }
979 
980 JSONGenerator::ObjectSP
DNBGetLibrariesInfoForAddresses(nub_process_t pid,std::vector<uint64_t> & macho_addresses)981 DNBGetLibrariesInfoForAddresses(nub_process_t pid,
982                                 std::vector<uint64_t> &macho_addresses) {
983   MachProcessSP procSP;
984   if (GetProcessSP(pid, procSP)) {
985     return procSP->GetLibrariesInfoForAddresses(pid, macho_addresses);
986   }
987   return JSONGenerator::ObjectSP();
988 }
989 
DNBGetSharedCacheInfo(nub_process_t pid)990 JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid) {
991   MachProcessSP procSP;
992   if (GetProcessSP(pid, procSP)) {
993     return procSP->GetSharedCacheInfo(pid);
994   }
995   return JSONGenerator::ObjectSP();
996 }
997 
DNBProcessGetExecutablePath(nub_process_t pid)998 const char *DNBProcessGetExecutablePath(nub_process_t pid) {
999   MachProcessSP procSP;
1000   if (GetProcessSP(pid, procSP)) {
1001     return procSP->Path();
1002   }
1003   return NULL;
1004 }
1005 
DNBProcessGetArgumentCount(nub_process_t pid)1006 nub_size_t DNBProcessGetArgumentCount(nub_process_t pid) {
1007   MachProcessSP procSP;
1008   if (GetProcessSP(pid, procSP)) {
1009     return procSP->ArgumentCount();
1010   }
1011   return 0;
1012 }
1013 
DNBProcessGetArgumentAtIndex(nub_process_t pid,nub_size_t idx)1014 const char *DNBProcessGetArgumentAtIndex(nub_process_t pid, nub_size_t idx) {
1015   MachProcessSP procSP;
1016   if (GetProcessSP(pid, procSP)) {
1017     return procSP->ArgumentAtIndex(idx);
1018   }
1019   return NULL;
1020 }
1021 
1022 // Execution control
DNBProcessResume(nub_process_t pid,const DNBThreadResumeAction * actions,size_t num_actions)1023 nub_bool_t DNBProcessResume(nub_process_t pid,
1024                             const DNBThreadResumeAction *actions,
1025                             size_t num_actions) {
1026   DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
1027   MachProcessSP procSP;
1028   if (GetProcessSP(pid, procSP)) {
1029     DNBThreadResumeActions thread_actions(actions, num_actions);
1030 
1031     // Below we add a default thread plan just in case one wasn't
1032     // provided so all threads always know what they were supposed to do
1033     if (thread_actions.IsEmpty()) {
1034       // No thread plans were given, so the default it to run all threads
1035       thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
1036     } else {
1037       // Some thread plans were given which means anything that wasn't
1038       // specified should remain stopped.
1039       thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
1040     }
1041     return procSP->Resume(thread_actions);
1042   }
1043   return false;
1044 }
1045 
DNBProcessHalt(nub_process_t pid)1046 nub_bool_t DNBProcessHalt(nub_process_t pid) {
1047   DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
1048   MachProcessSP procSP;
1049   if (GetProcessSP(pid, procSP))
1050     return procSP->Signal(SIGSTOP);
1051   return false;
1052 }
1053 //
1054 // nub_bool_t
1055 // DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)
1056 //{
1057 //    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)",
1058 //    __FUNCTION__, pid, tid, (uint32_t)step);
1059 //    MachProcessSP procSP;
1060 //    if (GetProcessSP (pid, procSP))
1061 //    {
1062 //        return procSP->Resume(tid, step, 0);
1063 //    }
1064 //    return false;
1065 //}
1066 //
1067 // nub_bool_t
1068 // DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t
1069 // step, int signal)
1070 //{
1071 //    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u,
1072 //    signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);
1073 //    MachProcessSP procSP;
1074 //    if (GetProcessSP (pid, procSP))
1075 //    {
1076 //        return procSP->Resume(tid, step, signal);
1077 //    }
1078 //    return false;
1079 //}
1080 
DNBProcessWaitForEvents(nub_process_t pid,nub_event_t event_mask,bool wait_for_set,struct timespec * timeout)1081 nub_event_t DNBProcessWaitForEvents(nub_process_t pid, nub_event_t event_mask,
1082                                     bool wait_for_set,
1083                                     struct timespec *timeout) {
1084   nub_event_t result = 0;
1085   MachProcessSP procSP;
1086   if (GetProcessSP(pid, procSP)) {
1087     if (wait_for_set)
1088       result = procSP->Events().WaitForSetEvents(event_mask, timeout);
1089     else
1090       result = procSP->Events().WaitForEventsToReset(event_mask, timeout);
1091   }
1092   return result;
1093 }
1094 
DNBProcessResetEvents(nub_process_t pid,nub_event_t event_mask)1095 void DNBProcessResetEvents(nub_process_t pid, nub_event_t event_mask) {
1096   MachProcessSP procSP;
1097   if (GetProcessSP(pid, procSP))
1098     procSP->Events().ResetEvents(event_mask);
1099 }
1100 
1101 // Breakpoints
DNBBreakpointSet(nub_process_t pid,nub_addr_t addr,nub_size_t size,nub_bool_t hardware)1102 nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
1103                             nub_bool_t hardware) {
1104   MachProcessSP procSP;
1105   if (GetProcessSP(pid, procSP))
1106     return procSP->CreateBreakpoint(addr, size, hardware) != NULL;
1107   return false;
1108 }
1109 
DNBBreakpointClear(nub_process_t pid,nub_addr_t addr)1110 nub_bool_t DNBBreakpointClear(nub_process_t pid, nub_addr_t addr) {
1111   MachProcessSP procSP;
1112   if (GetProcessSP(pid, procSP))
1113     return procSP->DisableBreakpoint(addr, true);
1114   return false; // Failed
1115 }
1116 
1117 // Watchpoints
DNBWatchpointSet(nub_process_t pid,nub_addr_t addr,nub_size_t size,uint32_t watch_flags,nub_bool_t hardware)1118 nub_bool_t DNBWatchpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
1119                             uint32_t watch_flags, nub_bool_t hardware) {
1120   MachProcessSP procSP;
1121   if (GetProcessSP(pid, procSP))
1122     return procSP->CreateWatchpoint(addr, size, watch_flags, hardware) != NULL;
1123   return false;
1124 }
1125 
DNBWatchpointClear(nub_process_t pid,nub_addr_t addr)1126 nub_bool_t DNBWatchpointClear(nub_process_t pid, nub_addr_t addr) {
1127   MachProcessSP procSP;
1128   if (GetProcessSP(pid, procSP))
1129     return procSP->DisableWatchpoint(addr, true);
1130   return false; // Failed
1131 }
1132 
1133 // Return the number of supported hardware watchpoints.
DNBWatchpointGetNumSupportedHWP(nub_process_t pid)1134 uint32_t DNBWatchpointGetNumSupportedHWP(nub_process_t pid) {
1135   MachProcessSP procSP;
1136   if (GetProcessSP(pid, procSP))
1137     return procSP->GetNumSupportedHardwareWatchpoints();
1138   return 0;
1139 }
1140 
1141 // Read memory in the address space of process PID. This call will take
1142 // care of setting and restoring permissions and breaking up the memory
1143 // read into multiple chunks as required.
1144 //
1145 // RETURNS: number of bytes actually read
DNBProcessMemoryRead(nub_process_t pid,nub_addr_t addr,nub_size_t size,void * buf)1146 nub_size_t DNBProcessMemoryRead(nub_process_t pid, nub_addr_t addr,
1147                                 nub_size_t size, void *buf) {
1148   MachProcessSP procSP;
1149   if (GetProcessSP(pid, procSP))
1150     return procSP->ReadMemory(addr, size, buf);
1151   return 0;
1152 }
1153 
DNBProcessMemoryReadInteger(nub_process_t pid,nub_addr_t addr,nub_size_t integer_size,uint64_t fail_value)1154 uint64_t DNBProcessMemoryReadInteger(nub_process_t pid, nub_addr_t addr,
1155                                      nub_size_t integer_size,
1156                                      uint64_t fail_value) {
1157   union Integers {
1158     uint8_t u8;
1159     uint16_t u16;
1160     uint32_t u32;
1161     uint64_t u64;
1162   };
1163 
1164   if (integer_size <= sizeof(uint64_t)) {
1165     Integers ints;
1166     if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) {
1167       switch (integer_size) {
1168       case 1:
1169         return ints.u8;
1170       case 2:
1171         return ints.u16;
1172       case 3:
1173         return ints.u32 & 0xffffffu;
1174       case 4:
1175         return ints.u32;
1176       case 5:
1177         return ints.u32 & 0x000000ffffffffffull;
1178       case 6:
1179         return ints.u32 & 0x0000ffffffffffffull;
1180       case 7:
1181         return ints.u32 & 0x00ffffffffffffffull;
1182       case 8:
1183         return ints.u64;
1184       }
1185     }
1186   }
1187   return fail_value;
1188 }
1189 
DNBProcessMemoryReadPointer(nub_process_t pid,nub_addr_t addr)1190 nub_addr_t DNBProcessMemoryReadPointer(nub_process_t pid, nub_addr_t addr) {
1191   cpu_type_t cputype = DNBProcessGetCPUType(pid);
1192   if (cputype) {
1193     const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4;
1194     return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0);
1195   }
1196   return 0;
1197 }
1198 
DNBProcessMemoryReadCString(nub_process_t pid,nub_addr_t addr)1199 std::string DNBProcessMemoryReadCString(nub_process_t pid, nub_addr_t addr) {
1200   std::string cstr;
1201   char buffer[256];
1202   const nub_size_t max_buffer_cstr_length = sizeof(buffer) - 1;
1203   buffer[max_buffer_cstr_length] = '\0';
1204   nub_size_t length = 0;
1205   nub_addr_t curr_addr = addr;
1206   do {
1207     nub_size_t bytes_read =
1208         DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer);
1209     if (bytes_read == 0)
1210       break;
1211     length = strlen(buffer);
1212     cstr.append(buffer, length);
1213     curr_addr += length;
1214   } while (length == max_buffer_cstr_length);
1215   return cstr;
1216 }
1217 
DNBProcessMemoryReadCStringFixed(nub_process_t pid,nub_addr_t addr,nub_size_t fixed_length)1218 std::string DNBProcessMemoryReadCStringFixed(nub_process_t pid, nub_addr_t addr,
1219                                              nub_size_t fixed_length) {
1220   std::string cstr;
1221   char buffer[fixed_length + 1];
1222   buffer[fixed_length] = '\0';
1223   nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer);
1224   if (bytes_read > 0)
1225     cstr.assign(buffer);
1226   return cstr;
1227 }
1228 
1229 // Write memory to the address space of process PID. This call will take
1230 // care of setting and restoring permissions and breaking up the memory
1231 // write into multiple chunks as required.
1232 //
1233 // RETURNS: number of bytes actually written
DNBProcessMemoryWrite(nub_process_t pid,nub_addr_t addr,nub_size_t size,const void * buf)1234 nub_size_t DNBProcessMemoryWrite(nub_process_t pid, nub_addr_t addr,
1235                                  nub_size_t size, const void *buf) {
1236   MachProcessSP procSP;
1237   if (GetProcessSP(pid, procSP))
1238     return procSP->WriteMemory(addr, size, buf);
1239   return 0;
1240 }
1241 
DNBProcessMemoryAllocate(nub_process_t pid,nub_size_t size,uint32_t permissions)1242 nub_addr_t DNBProcessMemoryAllocate(nub_process_t pid, nub_size_t size,
1243                                     uint32_t permissions) {
1244   MachProcessSP procSP;
1245   if (GetProcessSP(pid, procSP))
1246     return procSP->Task().AllocateMemory(size, permissions);
1247   return 0;
1248 }
1249 
DNBProcessMemoryDeallocate(nub_process_t pid,nub_addr_t addr)1250 nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid, nub_addr_t addr) {
1251   MachProcessSP procSP;
1252   if (GetProcessSP(pid, procSP))
1253     return procSP->Task().DeallocateMemory(addr);
1254   return 0;
1255 }
1256 
1257 // Find attributes of the memory region that contains ADDR for process PID,
1258 // if possible, and return a string describing those attributes.
1259 //
1260 // Returns 1 if we could find attributes for this region and OUTBUF can
1261 // be sent to the remote debugger.
1262 //
1263 // Returns 0 if we couldn't find the attributes for a region of memory at
1264 // that address and OUTBUF should not be sent.
1265 //
1266 // Returns -1 if this platform cannot look up information about memory regions
1267 // or if we do not yet have a valid launched process.
1268 //
DNBProcessMemoryRegionInfo(nub_process_t pid,nub_addr_t addr,DNBRegionInfo * region_info)1269 int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
1270                                DNBRegionInfo *region_info) {
1271   MachProcessSP procSP;
1272   if (GetProcessSP(pid, procSP))
1273     return procSP->Task().GetMemoryRegionInfo(addr, region_info);
1274 
1275   return -1;
1276 }
1277 
DNBProcessGetProfileData(nub_process_t pid,DNBProfileDataScanType scanType)1278 std::string DNBProcessGetProfileData(nub_process_t pid,
1279                                      DNBProfileDataScanType scanType) {
1280   MachProcessSP procSP;
1281   if (GetProcessSP(pid, procSP))
1282     return procSP->Task().GetProfileData(scanType);
1283 
1284   return std::string("");
1285 }
1286 
DNBProcessSetEnableAsyncProfiling(nub_process_t pid,nub_bool_t enable,uint64_t interval_usec,DNBProfileDataScanType scan_type)1287 nub_bool_t DNBProcessSetEnableAsyncProfiling(nub_process_t pid,
1288                                              nub_bool_t enable,
1289                                              uint64_t interval_usec,
1290                                              DNBProfileDataScanType scan_type) {
1291   MachProcessSP procSP;
1292   if (GetProcessSP(pid, procSP)) {
1293     procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);
1294     return true;
1295   }
1296 
1297   return false;
1298 }
1299 
1300 // Get the number of threads for the specified process.
DNBProcessGetNumThreads(nub_process_t pid)1301 nub_size_t DNBProcessGetNumThreads(nub_process_t pid) {
1302   MachProcessSP procSP;
1303   if (GetProcessSP(pid, procSP))
1304     return procSP->GetNumThreads();
1305   return 0;
1306 }
1307 
1308 // Get the thread ID of the current thread.
DNBProcessGetCurrentThread(nub_process_t pid)1309 nub_thread_t DNBProcessGetCurrentThread(nub_process_t pid) {
1310   MachProcessSP procSP;
1311   if (GetProcessSP(pid, procSP))
1312     return procSP->GetCurrentThread();
1313   return 0;
1314 }
1315 
1316 // Get the mach port number of the current thread.
DNBProcessGetCurrentThreadMachPort(nub_process_t pid)1317 nub_thread_t DNBProcessGetCurrentThreadMachPort(nub_process_t pid) {
1318   MachProcessSP procSP;
1319   if (GetProcessSP(pid, procSP))
1320     return procSP->GetCurrentThreadMachPort();
1321   return 0;
1322 }
1323 
1324 // Change the current thread.
DNBProcessSetCurrentThread(nub_process_t pid,nub_thread_t tid)1325 nub_thread_t DNBProcessSetCurrentThread(nub_process_t pid, nub_thread_t tid) {
1326   MachProcessSP procSP;
1327   if (GetProcessSP(pid, procSP))
1328     return procSP->SetCurrentThread(tid);
1329   return INVALID_NUB_THREAD;
1330 }
1331 
1332 // Dump a string describing a thread's stop reason to the specified file
1333 // handle
DNBThreadGetStopReason(nub_process_t pid,nub_thread_t tid,struct DNBThreadStopInfo * stop_info)1334 nub_bool_t DNBThreadGetStopReason(nub_process_t pid, nub_thread_t tid,
1335                                   struct DNBThreadStopInfo *stop_info) {
1336   MachProcessSP procSP;
1337   if (GetProcessSP(pid, procSP))
1338     return procSP->GetThreadStoppedReason(tid, stop_info);
1339   return false;
1340 }
1341 
1342 // Return string description for the specified thread.
1343 //
1344 // RETURNS: NULL if the thread isn't valid, else a NULL terminated C
1345 // string from a static buffer that must be copied prior to subsequent
1346 // calls.
DNBThreadGetInfo(nub_process_t pid,nub_thread_t tid)1347 const char *DNBThreadGetInfo(nub_process_t pid, nub_thread_t tid) {
1348   MachProcessSP procSP;
1349   if (GetProcessSP(pid, procSP))
1350     return procSP->GetThreadInfo(tid);
1351   return NULL;
1352 }
1353 
1354 // Get the thread ID given a thread index.
DNBProcessGetThreadAtIndex(nub_process_t pid,size_t thread_idx)1355 nub_thread_t DNBProcessGetThreadAtIndex(nub_process_t pid, size_t thread_idx) {
1356   MachProcessSP procSP;
1357   if (GetProcessSP(pid, procSP))
1358     return procSP->GetThreadAtIndex(thread_idx);
1359   return INVALID_NUB_THREAD;
1360 }
1361 
1362 // Do whatever is needed to sync the thread's register state with it's kernel
1363 // values.
DNBProcessSyncThreadState(nub_process_t pid,nub_thread_t tid)1364 nub_bool_t DNBProcessSyncThreadState(nub_process_t pid, nub_thread_t tid) {
1365   MachProcessSP procSP;
1366   if (GetProcessSP(pid, procSP))
1367     return procSP->SyncThreadState(tid);
1368   return false;
1369 }
1370 
DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid)1371 nub_addr_t DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid) {
1372   MachProcessSP procSP;
1373   DNBError err;
1374   if (GetProcessSP(pid, procSP))
1375     return procSP->Task().GetDYLDAllImageInfosAddress(err);
1376   return INVALID_NUB_ADDRESS;
1377 }
1378 
DNBProcessSharedLibrariesUpdated(nub_process_t pid)1379 nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) {
1380   MachProcessSP procSP;
1381   if (GetProcessSP(pid, procSP)) {
1382     procSP->SharedLibrariesUpdated();
1383     return true;
1384   }
1385   return false;
1386 }
1387 
DNBGetDeploymentInfo(nub_process_t pid,const struct load_command & lc,uint64_t load_command_address,uint32_t & major_version,uint32_t & minor_version,uint32_t & patch_version)1388 const char *DNBGetDeploymentInfo(nub_process_t pid,
1389                                  const struct load_command& lc,
1390                                  uint64_t load_command_address,
1391                                  uint32_t& major_version,
1392                                  uint32_t& minor_version,
1393                                  uint32_t& patch_version) {
1394   MachProcessSP procSP;
1395   if (GetProcessSP(pid, procSP))
1396     return procSP->GetDeploymentInfo(lc, load_command_address,
1397                                      major_version, minor_version,
1398                                      patch_version);
1399   return nullptr;
1400 }
1401 
1402 
1403 // Get the current shared library information for a process. Only return
1404 // the shared libraries that have changed since the last shared library
1405 // state changed event if only_changed is non-zero.
1406 nub_size_t
DNBProcessGetSharedLibraryInfo(nub_process_t pid,nub_bool_t only_changed,struct DNBExecutableImageInfo ** image_infos)1407 DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,
1408                                struct DNBExecutableImageInfo **image_infos) {
1409   MachProcessSP procSP;
1410   if (GetProcessSP(pid, procSP))
1411     return procSP->CopyImageInfos(image_infos, only_changed);
1412 
1413   // If we have no process, then return NULL for the shared library info
1414   // and zero for shared library count
1415   *image_infos = NULL;
1416   return 0;
1417 }
1418 
DNBGetRegisterCPUType()1419 uint32_t DNBGetRegisterCPUType() {
1420   return DNBArchProtocol::GetRegisterCPUType();
1421 }
1422 // Get the register set information for a specific thread.
DNBGetRegisterSetInfo(nub_size_t * num_reg_sets)1423 const DNBRegisterSetInfo *DNBGetRegisterSetInfo(nub_size_t *num_reg_sets) {
1424   return DNBArchProtocol::GetRegisterSetInfo(num_reg_sets);
1425 }
1426 
1427 // Read a register value by register set and register index.
DNBThreadGetRegisterValueByID(nub_process_t pid,nub_thread_t tid,uint32_t set,uint32_t reg,DNBRegisterValue * value)1428 nub_bool_t DNBThreadGetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
1429                                          uint32_t set, uint32_t reg,
1430                                          DNBRegisterValue *value) {
1431   MachProcessSP procSP;
1432   ::bzero(value, sizeof(DNBRegisterValue));
1433   if (GetProcessSP(pid, procSP)) {
1434     if (tid != INVALID_NUB_THREAD)
1435       return procSP->GetRegisterValue(tid, set, reg, value);
1436   }
1437   return false;
1438 }
1439 
DNBThreadSetRegisterValueByID(nub_process_t pid,nub_thread_t tid,uint32_t set,uint32_t reg,const DNBRegisterValue * value)1440 nub_bool_t DNBThreadSetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
1441                                          uint32_t set, uint32_t reg,
1442                                          const DNBRegisterValue *value) {
1443   if (tid != INVALID_NUB_THREAD) {
1444     MachProcessSP procSP;
1445     if (GetProcessSP(pid, procSP))
1446       return procSP->SetRegisterValue(tid, set, reg, value);
1447   }
1448   return false;
1449 }
1450 
DNBThreadGetRegisterContext(nub_process_t pid,nub_thread_t tid,void * buf,size_t buf_len)1451 nub_size_t DNBThreadGetRegisterContext(nub_process_t pid, nub_thread_t tid,
1452                                        void *buf, size_t buf_len) {
1453   MachProcessSP procSP;
1454   if (GetProcessSP(pid, procSP)) {
1455     if (tid != INVALID_NUB_THREAD)
1456       return procSP->GetThreadList().GetRegisterContext(tid, buf, buf_len);
1457   }
1458   ::bzero(buf, buf_len);
1459   return 0;
1460 }
1461 
DNBThreadSetRegisterContext(nub_process_t pid,nub_thread_t tid,const void * buf,size_t buf_len)1462 nub_size_t DNBThreadSetRegisterContext(nub_process_t pid, nub_thread_t tid,
1463                                        const void *buf, size_t buf_len) {
1464   MachProcessSP procSP;
1465   if (GetProcessSP(pid, procSP)) {
1466     if (tid != INVALID_NUB_THREAD)
1467       return procSP->GetThreadList().SetRegisterContext(tid, buf, buf_len);
1468   }
1469   return 0;
1470 }
1471 
DNBThreadSaveRegisterState(nub_process_t pid,nub_thread_t tid)1472 uint32_t DNBThreadSaveRegisterState(nub_process_t pid, nub_thread_t tid) {
1473   if (tid != INVALID_NUB_THREAD) {
1474     MachProcessSP procSP;
1475     if (GetProcessSP(pid, procSP))
1476       return procSP->GetThreadList().SaveRegisterState(tid);
1477   }
1478   return 0;
1479 }
DNBThreadRestoreRegisterState(nub_process_t pid,nub_thread_t tid,uint32_t save_id)1480 nub_bool_t DNBThreadRestoreRegisterState(nub_process_t pid, nub_thread_t tid,
1481                                          uint32_t save_id) {
1482   if (tid != INVALID_NUB_THREAD) {
1483     MachProcessSP procSP;
1484     if (GetProcessSP(pid, procSP))
1485       return procSP->GetThreadList().RestoreRegisterState(tid, save_id);
1486   }
1487   return false;
1488 }
1489 
1490 // Read a register value by name.
DNBThreadGetRegisterValueByName(nub_process_t pid,nub_thread_t tid,uint32_t reg_set,const char * reg_name,DNBRegisterValue * value)1491 nub_bool_t DNBThreadGetRegisterValueByName(nub_process_t pid, nub_thread_t tid,
1492                                            uint32_t reg_set,
1493                                            const char *reg_name,
1494                                            DNBRegisterValue *value) {
1495   MachProcessSP procSP;
1496   ::bzero(value, sizeof(DNBRegisterValue));
1497   if (GetProcessSP(pid, procSP)) {
1498     const struct DNBRegisterSetInfo *set_info;
1499     nub_size_t num_reg_sets = 0;
1500     set_info = DNBGetRegisterSetInfo(&num_reg_sets);
1501     if (set_info) {
1502       uint32_t set = reg_set;
1503       uint32_t reg;
1504       if (set == REGISTER_SET_ALL) {
1505         for (set = 1; set < num_reg_sets; ++set) {
1506           for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1507             if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1508               return procSP->GetRegisterValue(tid, set, reg, value);
1509           }
1510         }
1511       } else {
1512         for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1513           if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1514             return procSP->GetRegisterValue(tid, set, reg, value);
1515         }
1516       }
1517     }
1518   }
1519   return false;
1520 }
1521 
1522 // Read a register set and register number from the register name.
DNBGetRegisterInfoByName(const char * reg_name,DNBRegisterInfo * info)1523 nub_bool_t DNBGetRegisterInfoByName(const char *reg_name,
1524                                     DNBRegisterInfo *info) {
1525   const struct DNBRegisterSetInfo *set_info;
1526   nub_size_t num_reg_sets = 0;
1527   set_info = DNBGetRegisterSetInfo(&num_reg_sets);
1528   if (set_info) {
1529     uint32_t set, reg;
1530     for (set = 1; set < num_reg_sets; ++set) {
1531       for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1532         if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) {
1533           *info = set_info[set].registers[reg];
1534           return true;
1535         }
1536       }
1537     }
1538 
1539     for (set = 1; set < num_reg_sets; ++set) {
1540       uint32_t reg;
1541       for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1542         if (set_info[set].registers[reg].alt == NULL)
1543           continue;
1544 
1545         if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0) {
1546           *info = set_info[set].registers[reg];
1547           return true;
1548         }
1549       }
1550     }
1551   }
1552 
1553   ::bzero(info, sizeof(DNBRegisterInfo));
1554   return false;
1555 }
1556 
1557 // Set the name to address callback function that this nub can use
1558 // for any name to address lookups that are needed.
DNBProcessSetNameToAddressCallback(nub_process_t pid,DNBCallbackNameToAddress callback,void * baton)1559 nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,
1560                                               DNBCallbackNameToAddress callback,
1561                                               void *baton) {
1562   MachProcessSP procSP;
1563   if (GetProcessSP(pid, procSP)) {
1564     procSP->SetNameToAddressCallback(callback, baton);
1565     return true;
1566   }
1567   return false;
1568 }
1569 
1570 // Set the name to address callback function that this nub can use
1571 // for any name to address lookups that are needed.
DNBProcessSetSharedLibraryInfoCallback(nub_process_t pid,DNBCallbackCopyExecutableImageInfos callback,void * baton)1572 nub_bool_t DNBProcessSetSharedLibraryInfoCallback(
1573     nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback,
1574     void *baton) {
1575   MachProcessSP procSP;
1576   if (GetProcessSP(pid, procSP)) {
1577     procSP->SetSharedLibraryInfoCallback(callback, baton);
1578     return true;
1579   }
1580   return false;
1581 }
1582 
DNBProcessLookupAddress(nub_process_t pid,const char * name,const char * shlib)1583 nub_addr_t DNBProcessLookupAddress(nub_process_t pid, const char *name,
1584                                    const char *shlib) {
1585   MachProcessSP procSP;
1586   if (GetProcessSP(pid, procSP)) {
1587     return procSP->LookupSymbol(name, shlib);
1588   }
1589   return INVALID_NUB_ADDRESS;
1590 }
1591 
DNBProcessGetAvailableSTDOUT(nub_process_t pid,char * buf,nub_size_t buf_size)1592 nub_size_t DNBProcessGetAvailableSTDOUT(nub_process_t pid, char *buf,
1593                                         nub_size_t buf_size) {
1594   MachProcessSP procSP;
1595   if (GetProcessSP(pid, procSP))
1596     return procSP->GetAvailableSTDOUT(buf, buf_size);
1597   return 0;
1598 }
1599 
DNBProcessGetAvailableSTDERR(nub_process_t pid,char * buf,nub_size_t buf_size)1600 nub_size_t DNBProcessGetAvailableSTDERR(nub_process_t pid, char *buf,
1601                                         nub_size_t buf_size) {
1602   MachProcessSP procSP;
1603   if (GetProcessSP(pid, procSP))
1604     return procSP->GetAvailableSTDERR(buf, buf_size);
1605   return 0;
1606 }
1607 
DNBProcessGetAvailableProfileData(nub_process_t pid,char * buf,nub_size_t buf_size)1608 nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,
1609                                              nub_size_t buf_size) {
1610   MachProcessSP procSP;
1611   if (GetProcessSP(pid, procSP))
1612     return procSP->GetAsyncProfileData(buf, buf_size);
1613   return 0;
1614 }
1615 
DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid)1616 DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid) {
1617   return DarwinLogCollector::GetEventsForProcess(pid);
1618 }
1619 
DNBProcessGetStopCount(nub_process_t pid)1620 nub_size_t DNBProcessGetStopCount(nub_process_t pid) {
1621   MachProcessSP procSP;
1622   if (GetProcessSP(pid, procSP))
1623     return procSP->StopCount();
1624   return 0;
1625 }
1626 
DNBProcessGetCPUType(nub_process_t pid)1627 uint32_t DNBProcessGetCPUType(nub_process_t pid) {
1628   MachProcessSP procSP;
1629   if (GetProcessSP(pid, procSP))
1630     return procSP->GetCPUType();
1631   return 0;
1632 }
1633 
DNBResolveExecutablePath(const char * path,char * resolved_path,size_t resolved_path_size)1634 nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path,
1635                                     size_t resolved_path_size) {
1636   if (path == NULL || path[0] == '\0')
1637     return false;
1638 
1639   char max_path[PATH_MAX];
1640   std::string result;
1641   CFString::GlobPath(path, result);
1642 
1643   if (result.empty())
1644     result = path;
1645 
1646   struct stat path_stat;
1647   if (::stat(path, &path_stat) == 0) {
1648     if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
1649       CFBundle bundle(path);
1650       CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
1651       if (url.get()) {
1652         if (::CFURLGetFileSystemRepresentation(
1653                 url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
1654           return true;
1655       }
1656     }
1657   }
1658 
1659   if (realpath(path, max_path)) {
1660     // Found the path relatively...
1661     ::strlcpy(resolved_path, max_path, resolved_path_size);
1662     return strlen(resolved_path) + 1 < resolved_path_size;
1663   } else {
1664     // Not a relative path, check the PATH environment variable if the
1665     const char *PATH = getenv("PATH");
1666     if (PATH) {
1667       const char *curr_path_start = PATH;
1668       const char *curr_path_end;
1669       while (curr_path_start && *curr_path_start) {
1670         curr_path_end = strchr(curr_path_start, ':');
1671         if (curr_path_end == NULL) {
1672           result.assign(curr_path_start);
1673           curr_path_start = NULL;
1674         } else if (curr_path_end > curr_path_start) {
1675           size_t len = curr_path_end - curr_path_start;
1676           result.assign(curr_path_start, len);
1677           curr_path_start += len + 1;
1678         } else
1679           break;
1680 
1681         result += '/';
1682         result += path;
1683         struct stat s;
1684         if (stat(result.c_str(), &s) == 0) {
1685           ::strlcpy(resolved_path, result.c_str(), resolved_path_size);
1686           return result.size() + 1 < resolved_path_size;
1687         }
1688       }
1689     }
1690   }
1691   return false;
1692 }
1693 
DNBGetOSVersionNumbers(uint64_t * major,uint64_t * minor,uint64_t * patch)1694 bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch) {
1695   return MachProcess::GetOSVersionNumbers(major, minor, patch);
1696 }
1697 
DNBGetMacCatalystVersionString()1698 std::string DNBGetMacCatalystVersionString() {
1699   return MachProcess::GetMacCatalystVersionString();
1700 }
1701 
DNBInitialize()1702 void DNBInitialize() {
1703   DNBLogThreadedIf(LOG_PROCESS, "DNBInitialize ()");
1704 #if defined(__i386__) || defined(__x86_64__)
1705   DNBArchImplI386::Initialize();
1706   DNBArchImplX86_64::Initialize();
1707 #elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
1708   DNBArchMachARM::Initialize();
1709   DNBArchMachARM64::Initialize();
1710 #endif
1711 }
1712 
DNBTerminate()1713 void DNBTerminate() {}
1714 
DNBSetArchitecture(const char * arch)1715 nub_bool_t DNBSetArchitecture(const char *arch) {
1716   if (arch && arch[0]) {
1717     if (strcasecmp(arch, "i386") == 0)
1718       return DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
1719     else if ((strcasecmp(arch, "x86_64") == 0) ||
1720              (strcasecmp(arch, "x86_64h") == 0))
1721       return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64);
1722     else if (strstr(arch, "arm64_32") == arch ||
1723              strstr(arch, "aarch64_32") == arch)
1724       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32);
1725     else if (strstr(arch, "arm64e") == arch)
1726       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
1727     else if (strstr(arch, "arm64") == arch || strstr(arch, "armv8") == arch ||
1728              strstr(arch, "aarch64") == arch)
1729       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
1730     else if (strstr(arch, "arm") == arch)
1731       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
1732   }
1733   return false;
1734 }
1735