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 static size_t
61 GetAllInfosMatchingName(const char *process_name,
62 std::vector<struct kinfo_proc> &matching_proc_infos);
63
64 // A Thread safe singleton to get a process map pointer.
65 //
66 // Returns a pointer to the existing process map, or a pointer to a
67 // newly created process map if CAN_CREATE is non-zero.
GetProcessMap(bool can_create)68 static ProcessMap *GetProcessMap(bool can_create) {
69 static ProcessMap *g_process_map_ptr = NULL;
70
71 if (can_create && g_process_map_ptr == NULL) {
72 static pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER;
73 PTHREAD_MUTEX_LOCKER(locker, &g_process_map_mutex);
74 if (g_process_map_ptr == NULL)
75 g_process_map_ptr = new ProcessMap;
76 }
77 return g_process_map_ptr;
78 }
79
80 // Add PID to the shared process pointer map.
81 //
82 // Return non-zero value if we succeed in adding the process to the map.
83 // The only time this should fail is if we run out of memory and can't
84 // allocate a ProcessMap.
AddProcessToMap(nub_process_t pid,MachProcessSP & procSP)85 static nub_bool_t AddProcessToMap(nub_process_t pid, MachProcessSP &procSP) {
86 ProcessMap *process_map = GetProcessMap(true);
87 if (process_map) {
88 process_map->insert(std::make_pair(pid, procSP));
89 return true;
90 }
91 return false;
92 }
93
94 // Remove the shared pointer for PID from the process map.
95 //
96 // Returns the number of items removed from the process map.
97 // static size_t
98 // RemoveProcessFromMap (nub_process_t pid)
99 //{
100 // ProcessMap* process_map = GetProcessMap(false);
101 // if (process_map)
102 // {
103 // return process_map->erase(pid);
104 // }
105 // return 0;
106 //}
107
108 // Get the shared pointer for PID from the existing process map.
109 //
110 // Returns true if we successfully find a shared pointer to a
111 // MachProcess object.
GetProcessSP(nub_process_t pid,MachProcessSP & procSP)112 static nub_bool_t GetProcessSP(nub_process_t pid, MachProcessSP &procSP) {
113 ProcessMap *process_map = GetProcessMap(false);
114 if (process_map != NULL) {
115 ProcessMapIter pos = process_map->find(pid);
116 if (pos != process_map->end()) {
117 procSP = pos->second;
118 return true;
119 }
120 }
121 procSP.reset();
122 return false;
123 }
124
125 #ifdef USE_KQUEUE
kqueue_thread(void * arg)126 void *kqueue_thread(void *arg) {
127 int kq_id = (int)(intptr_t)arg;
128
129 #if defined(__APPLE__)
130 pthread_setname_np("kqueue thread");
131 #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
132 struct sched_param thread_param;
133 int thread_sched_policy;
134 if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
135 &thread_param) == 0) {
136 thread_param.sched_priority = 47;
137 pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
138 }
139 #endif
140 #endif
141
142 struct kevent death_event;
143 while (true) {
144 int n_events = kevent(kq_id, NULL, 0, &death_event, 1, NULL);
145 if (n_events == -1) {
146 if (errno == EINTR)
147 continue;
148 else {
149 DNBLogError("kqueue failed with error: (%d): %s", errno,
150 strerror(errno));
151 return NULL;
152 }
153 } else if (death_event.flags & EV_ERROR) {
154 int error_no = static_cast<int>(death_event.data);
155 const char *error_str = strerror(error_no);
156 if (error_str == NULL)
157 error_str = "Unknown error";
158 DNBLogError("Failed to initialize kqueue event: (%d): %s", error_no,
159 error_str);
160 return NULL;
161 } else {
162 int status;
163 const pid_t pid = (pid_t)death_event.ident;
164 const pid_t child_pid = waitpid(pid, &status, 0);
165
166 bool exited = false;
167 int signal = 0;
168 int exit_status = 0;
169 if (WIFSTOPPED(status)) {
170 signal = WSTOPSIG(status);
171 DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> STOPPED (signal = %i)",
172 child_pid, signal);
173 } else if (WIFEXITED(status)) {
174 exit_status = WEXITSTATUS(status);
175 exited = true;
176 DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> EXITED (status = %i)",
177 child_pid, exit_status);
178 } else if (WIFSIGNALED(status)) {
179 signal = WTERMSIG(status);
180 if (child_pid == abs(pid)) {
181 DNBLogThreadedIf(LOG_PROCESS,
182 "waitpid (%i) -> SIGNALED and EXITED (signal = %i)",
183 child_pid, signal);
184 char exit_info[64];
185 ::snprintf(exit_info, sizeof(exit_info),
186 "Terminated due to signal %i", signal);
187 DNBProcessSetExitInfo(child_pid, exit_info);
188 exited = true;
189 exit_status = INT8_MAX;
190 } else {
191 DNBLogThreadedIf(LOG_PROCESS,
192 "waitpid (%i) -> SIGNALED (signal = %i)", child_pid,
193 signal);
194 }
195 }
196
197 if (exited) {
198 if (death_event.data & NOTE_EXIT_MEMORY)
199 DNBProcessSetExitInfo(child_pid, "Terminated due to memory issue");
200 else if (death_event.data & NOTE_EXIT_DECRYPTFAIL)
201 DNBProcessSetExitInfo(child_pid, "Terminated due to decrypt failure");
202 else if (death_event.data & NOTE_EXIT_CSERROR)
203 DNBProcessSetExitInfo(child_pid,
204 "Terminated due to code signing error");
205
206 DNBLogThreadedIf(
207 LOG_PROCESS,
208 "waitpid_process_thread (): setting exit status for pid = %i to %i",
209 child_pid, exit_status);
210 DNBProcessSetExitStatus(child_pid, status);
211 return NULL;
212 }
213 }
214 }
215 }
216
spawn_kqueue_thread(pid_t pid)217 static bool spawn_kqueue_thread(pid_t pid) {
218 pthread_t thread;
219 int kq_id;
220
221 kq_id = kqueue();
222 if (kq_id == -1) {
223 DNBLogError("Could not get kqueue for pid = %i.", pid);
224 return false;
225 }
226
227 struct kevent reg_event;
228
229 EV_SET(®_event, pid, EVFILT_PROC, EV_ADD,
230 NOTE_EXIT | NOTE_EXITSTATUS | NOTE_EXIT_DETAIL, 0, NULL);
231 // Register the event:
232 int result = kevent(kq_id, ®_event, 1, NULL, 0, NULL);
233 if (result != 0) {
234 DNBLogError(
235 "Failed to register kqueue NOTE_EXIT event for pid %i, error: %d.", pid,
236 result);
237 return false;
238 }
239
240 int ret =
241 ::pthread_create(&thread, NULL, kqueue_thread, (void *)(intptr_t)kq_id);
242
243 // pthread_create returns 0 if successful
244 if (ret == 0) {
245 ::pthread_detach(thread);
246 return true;
247 }
248 return false;
249 }
250 #endif // #if USE_KQUEUE
251
waitpid_thread(void * arg)252 static void *waitpid_thread(void *arg) {
253 const pid_t pid = (pid_t)(intptr_t)arg;
254 int status;
255
256 #if defined(__APPLE__)
257 pthread_setname_np("waitpid thread");
258 #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
259 struct sched_param thread_param;
260 int thread_sched_policy;
261 if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
262 &thread_param) == 0) {
263 thread_param.sched_priority = 47;
264 pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
265 }
266 #endif
267 #endif
268
269 while (true) {
270 pid_t child_pid = waitpid(pid, &status, 0);
271 DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): waitpid (pid = %i, "
272 "&status, 0) => %i, status = %i, errno = %i",
273 pid, child_pid, status, errno);
274
275 if (child_pid < 0) {
276 if (errno == EINTR)
277 continue;
278 break;
279 } else {
280 if (WIFSTOPPED(status)) {
281 continue;
282 } else // if (WIFEXITED(status) || WIFSIGNALED(status))
283 {
284 DNBLogThreadedIf(
285 LOG_PROCESS,
286 "waitpid_thread (): setting exit status for pid = %i to %i",
287 child_pid, status);
288 DNBProcessSetExitStatus(child_pid, status);
289 return NULL;
290 }
291 }
292 }
293
294 // We should never exit as long as our child process is alive, so if we
295 // do something else went wrong and we should exit...
296 DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): main loop exited, setting "
297 "exit status to an invalid value (-1) for pid "
298 "%i",
299 pid);
300 DNBProcessSetExitStatus(pid, -1);
301 return NULL;
302 }
spawn_waitpid_thread(pid_t pid)303 static bool spawn_waitpid_thread(pid_t pid) {
304 #ifdef USE_KQUEUE
305 bool success = spawn_kqueue_thread(pid);
306 if (success)
307 return true;
308 #endif
309
310 pthread_t thread;
311 int ret =
312 ::pthread_create(&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);
313 // pthread_create returns 0 if successful
314 if (ret == 0) {
315 ::pthread_detach(thread);
316 return true;
317 }
318 return false;
319 }
320
DNBProcessLaunch(RNBContext * ctx,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,int disable_aslr,const char * event_data,char * err_str,size_t err_len)321 nub_process_t DNBProcessLaunch(
322 RNBContext *ctx, const char *path, char const *argv[], const char *envp[],
323 const char *working_directory, // NULL => don't change, non-NULL => set
324 // working directory for inferior to this
325 const char *stdin_path, const char *stdout_path, const char *stderr_path,
326 bool no_stdio, int disable_aslr, const char *event_data, char *err_str,
327 size_t err_len) {
328 DNBLogThreadedIf(LOG_PROCESS,
329 "%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, ctx->LaunchFlavor(),
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(
354 path, argv, envp, working_directory, stdin_path, stdout_path,
355 stderr_path, no_stdio, ctx->LaunchFlavor(), disable_aslr, event_data,
356 ctx->GetUnmaskSignals(), 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,bool unmask_signals,char * err_str,size_t err_len)415 nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout,
416 bool unmask_signals, char *err_str,
417 size_t err_len) {
418 if (err_str && err_len > 0)
419 err_str[0] = '\0';
420 std::vector<struct kinfo_proc> matching_proc_infos;
421 size_t num_matching_proc_infos =
422 GetAllInfosMatchingName(name, matching_proc_infos);
423 if (num_matching_proc_infos == 0) {
424 DNBLogError("error: no processes match '%s'\n", name);
425 return INVALID_NUB_PROCESS;
426 }
427 if (num_matching_proc_infos > 1) {
428 DNBLogError("error: %llu processes match '%s':\n",
429 (uint64_t)num_matching_proc_infos, name);
430 size_t i;
431 for (i = 0; i < num_matching_proc_infos; ++i)
432 DNBLogError("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid,
433 matching_proc_infos[i].kp_proc.p_comm);
434 return INVALID_NUB_PROCESS;
435 }
436
437 return DNBProcessAttach(matching_proc_infos[0].kp_proc.p_pid, timeout,
438 unmask_signals, err_str, err_len);
439 }
440
DNBProcessAttach(nub_process_t attach_pid,struct timespec * timeout,bool unmask_signals,char * err_str,size_t err_len)441 nub_process_t DNBProcessAttach(nub_process_t attach_pid,
442 struct timespec *timeout, bool unmask_signals,
443 char *err_str, size_t err_len) {
444 if (err_str && err_len > 0)
445 err_str[0] = '\0';
446
447 if (getenv("LLDB_DEBUGSERVER_PATH") == NULL) {
448 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID,
449 static_cast<int>(attach_pid)};
450 struct kinfo_proc processInfo;
451 size_t bufsize = sizeof(processInfo);
452 if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo,
453 &bufsize, NULL, 0) == 0 &&
454 bufsize > 0) {
455
456 if ((processInfo.kp_proc.p_flag & P_TRANSLATED) == P_TRANSLATED) {
457 const char *translated_debugserver =
458 "/Library/Apple/usr/libexec/oah/debugserver";
459 char fdstr[16];
460 char pidstr[16];
461 extern int communication_fd;
462
463 if (communication_fd == -1) {
464 fprintf(stderr, "Trying to attach to a translated process with the "
465 "native debugserver, exiting...\n");
466 exit(1);
467 }
468
469 snprintf(fdstr, sizeof(fdstr), "--fd=%d", communication_fd);
470 snprintf(pidstr, sizeof(pidstr), "--attach=%d", attach_pid);
471 execl(translated_debugserver, "--native-regs", "--setsid", fdstr,
472 "--handoff-attach-from-native", pidstr, (char *)0);
473 DNBLogThreadedIf(LOG_PROCESS, "Failed to launch debugserver for "
474 "translated process: ", errno, strerror(errno));
475 __builtin_trap();
476 }
477 }
478 }
479
480 pid_t pid = INVALID_NUB_PROCESS;
481 MachProcessSP processSP(new MachProcess);
482 if (processSP.get()) {
483 DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...",
484 attach_pid);
485 pid =
486 processSP->AttachForDebug(attach_pid, unmask_signals, err_str, err_len);
487
488 if (pid != INVALID_NUB_PROCESS) {
489 bool res = AddProcessToMap(pid, processSP);
490 UNUSED_IF_ASSERT_DISABLED(res);
491 assert(res && "Couldn't add process to map!");
492 spawn_waitpid_thread(pid);
493 }
494 }
495
496 while (pid != INVALID_NUB_PROCESS) {
497 // Wait for process to start up and hit entry point
498 DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
499 "eEventProcessRunningStateChanged | "
500 "eEventProcessStoppedStateChanged, true, "
501 "INFINITE)...",
502 __FUNCTION__, pid);
503 nub_event_t set_events =
504 DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |
505 eEventProcessStoppedStateChanged,
506 true, timeout);
507
508 DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
509 "eEventProcessRunningStateChanged | "
510 "eEventProcessStoppedStateChanged, true, "
511 "INFINITE) => 0x%8.8x",
512 __FUNCTION__, pid, set_events);
513
514 if (set_events == 0) {
515 if (err_str && err_len > 0)
516 snprintf(err_str, err_len, "operation timed out");
517 pid = INVALID_NUB_PROCESS;
518 } else {
519 if (set_events & (eEventProcessRunningStateChanged |
520 eEventProcessStoppedStateChanged)) {
521 nub_state_t pid_state = DNBProcessGetState(pid);
522 DNBLogThreadedIf(
523 LOG_PROCESS,
524 "%s process %4.4x state changed (eEventProcessStateChanged): %s",
525 __FUNCTION__, pid, DNBStateAsString(pid_state));
526
527 switch (pid_state) {
528 case eStateInvalid:
529 case eStateUnloaded:
530 case eStateAttaching:
531 case eStateLaunching:
532 case eStateSuspended:
533 break; // Ignore
534
535 case eStateRunning:
536 case eStateStepping:
537 // Still waiting to stop at entry point...
538 break;
539
540 case eStateStopped:
541 case eStateCrashed:
542 return pid;
543
544 case eStateDetached:
545 case eStateExited:
546 if (err_str && err_len > 0)
547 snprintf(err_str, err_len, "process exited");
548 return INVALID_NUB_PROCESS;
549 }
550 }
551
552 DNBProcessResetEvents(pid, set_events);
553 }
554 }
555
556 return INVALID_NUB_PROCESS;
557 }
558
DNBGetAllInfos(std::vector<struct kinfo_proc> & proc_infos)559 size_t DNBGetAllInfos(std::vector<struct kinfo_proc> &proc_infos) {
560 size_t size = 0;
561 int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
562 u_int namelen = sizeof(name) / sizeof(int);
563 int err;
564
565 // Try to find out how many processes are around so we can
566 // size the buffer appropriately. sysctl's man page specifically suggests
567 // this approach, and says it returns a bit larger size than needed to
568 // handle any new processes created between then and now.
569
570 err = ::sysctl(name, namelen, NULL, &size, NULL, 0);
571
572 if ((err < 0) && (err != ENOMEM)) {
573 proc_infos.clear();
574 perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");
575 return 0;
576 }
577
578 // Increase the size of the buffer by a few processes in case more have
579 // been spawned
580 proc_infos.resize(size / sizeof(struct kinfo_proc));
581 size = proc_infos.size() *
582 sizeof(struct kinfo_proc); // Make sure we don't exceed our resize...
583 err = ::sysctl(name, namelen, &proc_infos[0], &size, NULL, 0);
584 if (err < 0) {
585 proc_infos.clear();
586 return 0;
587 }
588
589 // Trim down our array to fit what we actually got back
590 proc_infos.resize(size / sizeof(struct kinfo_proc));
591 return proc_infos.size();
592 }
593
594 static size_t
GetAllInfosMatchingName(const char * full_process_name,std::vector<struct kinfo_proc> & matching_proc_infos)595 GetAllInfosMatchingName(const char *full_process_name,
596 std::vector<struct kinfo_proc> &matching_proc_infos) {
597
598 matching_proc_infos.clear();
599 if (full_process_name && full_process_name[0]) {
600 // We only get the process name, not the full path, from the proc_info. So
601 // just take the
602 // base name of the process name...
603 const char *process_name;
604 process_name = strrchr(full_process_name, '/');
605 if (process_name == NULL)
606 process_name = full_process_name;
607 else
608 process_name++;
609
610 const size_t process_name_len = strlen(process_name);
611 std::vector<struct kinfo_proc> proc_infos;
612 const size_t num_proc_infos = DNBGetAllInfos(proc_infos);
613 if (num_proc_infos > 0) {
614 uint32_t i;
615 for (i = 0; i < num_proc_infos; i++) {
616 // Skip zombie processes and processes with unset status
617 if (proc_infos[i].kp_proc.p_stat == 0 ||
618 proc_infos[i].kp_proc.p_stat == SZOMB)
619 continue;
620
621 // Check for process by name. We only check the first MAXCOMLEN
622 // chars as that is all that kp_proc.p_comm holds.
623
624 if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm,
625 MAXCOMLEN) == 0) {
626 if (process_name_len > MAXCOMLEN) {
627 // We found a matching process name whose first MAXCOMLEN
628 // characters match, but there is more to the name than
629 // this. We need to get the full process name. Use proc_pidpath,
630 // which will get
631 // us the full path to the executed process.
632
633 char proc_path_buf[PATH_MAX];
634
635 int return_val = proc_pidpath(proc_infos[i].kp_proc.p_pid,
636 proc_path_buf, PATH_MAX);
637 if (return_val > 0) {
638 // Okay, now search backwards from that to see if there is a
639 // slash in the name. Note, even though we got all the args we
640 // don't care
641 // because the list data is just a bunch of concatenated null
642 // terminated strings
643 // so strrchr will start from the end of argv0.
644
645 const char *argv_basename = strrchr(proc_path_buf, '/');
646 if (argv_basename) {
647 // Skip the '/'
648 ++argv_basename;
649 } else {
650 // We didn't find a directory delimiter in the process argv[0],
651 // just use what was in there
652 argv_basename = proc_path_buf;
653 }
654
655 if (argv_basename) {
656 if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0) {
657 matching_proc_infos.push_back(proc_infos[i]);
658 }
659 }
660 }
661 } else {
662 // We found a matching process, add it to our list
663 matching_proc_infos.push_back(proc_infos[i]);
664 }
665 }
666 }
667 }
668 }
669 // return the newly added matches.
670 return matching_proc_infos.size();
671 }
672
673 nub_process_t
DNBProcessAttachWait(RNBContext * ctx,const char * waitfor_process_name,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)674 DNBProcessAttachWait(RNBContext *ctx, const char *waitfor_process_name,
675 bool ignore_existing, struct timespec *timeout_abstime,
676 useconds_t waitfor_interval, char *err_str, size_t err_len,
677 DNBShouldCancelCallback should_cancel_callback,
678 void *callback_data) {
679 DNBError prepare_error;
680 std::vector<struct kinfo_proc> exclude_proc_infos;
681 size_t num_exclude_proc_infos;
682
683 nub_launch_flavor_t launch_flavor = ctx->LaunchFlavor();
684
685 // If the PrepareForAttach returns a valid token, use MachProcess to check
686 // for the process, otherwise scan the process table.
687
688 const void *attach_token = MachProcess::PrepareForAttach(
689 waitfor_process_name, launch_flavor, true, prepare_error);
690
691 if (prepare_error.Fail()) {
692 DNBLogError("Error in PrepareForAttach: %s", prepare_error.AsString());
693 return INVALID_NUB_PROCESS;
694 }
695
696 if (attach_token == NULL) {
697 if (ignore_existing)
698 num_exclude_proc_infos =
699 GetAllInfosMatchingName(waitfor_process_name, exclude_proc_infos);
700 else
701 num_exclude_proc_infos = 0;
702 }
703
704 DNBLogThreadedIf(LOG_PROCESS, "Waiting for '%s' to appear...\n",
705 waitfor_process_name);
706
707 // Loop and try to find the process by name
708 nub_process_t waitfor_pid = INVALID_NUB_PROCESS;
709
710 while (waitfor_pid == INVALID_NUB_PROCESS) {
711 if (attach_token != NULL) {
712 nub_process_t pid;
713 pid = MachProcess::CheckForProcess(attach_token, launch_flavor);
714 if (pid != INVALID_NUB_PROCESS) {
715 waitfor_pid = pid;
716 break;
717 }
718 } else {
719
720 // Get the current process list, and check for matches that
721 // aren't in our original list. If anyone wants to attach
722 // to an existing process by name, they should do it with
723 // --attach=PROCNAME. Else we will wait for the first matching
724 // process that wasn't in our exclusion list.
725 std::vector<struct kinfo_proc> proc_infos;
726 const size_t num_proc_infos =
727 GetAllInfosMatchingName(waitfor_process_name, proc_infos);
728 for (size_t i = 0; i < num_proc_infos; i++) {
729 nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;
730 for (size_t j = 0; j < num_exclude_proc_infos; j++) {
731 if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid) {
732 // This process was in our exclusion list, don't use it.
733 curr_pid = INVALID_NUB_PROCESS;
734 break;
735 }
736 }
737
738 // If we didn't find CURR_PID in our exclusion list, then use it.
739 if (curr_pid != INVALID_NUB_PROCESS) {
740 // We found our process!
741 waitfor_pid = curr_pid;
742 break;
743 }
744 }
745 }
746
747 // If we haven't found our process yet, check for a timeout
748 // and then sleep for a bit until we poll again.
749 if (waitfor_pid == INVALID_NUB_PROCESS) {
750 if (timeout_abstime != NULL) {
751 // Check to see if we have a waitfor-duration option that
752 // has timed out?
753 if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime)) {
754 if (err_str && err_len > 0)
755 snprintf(err_str, err_len, "operation timed out");
756 DNBLogError("error: waiting for process '%s' timed out.\n",
757 waitfor_process_name);
758 return INVALID_NUB_PROCESS;
759 }
760 }
761
762 // Call the should cancel callback as well...
763
764 if (should_cancel_callback != NULL &&
765 should_cancel_callback(callback_data)) {
766 DNBLogThreadedIf(
767 LOG_PROCESS,
768 "DNBProcessAttachWait cancelled by should_cancel callback.");
769 waitfor_pid = INVALID_NUB_PROCESS;
770 break;
771 }
772
773 ::usleep(waitfor_interval); // Sleep for WAITFOR_INTERVAL, then poll again
774 }
775 }
776
777 if (waitfor_pid != INVALID_NUB_PROCESS) {
778 DNBLogThreadedIf(LOG_PROCESS, "Attaching to %s with pid %i...\n",
779 waitfor_process_name, waitfor_pid);
780 waitfor_pid = DNBProcessAttach(waitfor_pid, timeout_abstime,
781 ctx->GetUnmaskSignals(), err_str, err_len);
782 }
783
784 bool success = waitfor_pid != INVALID_NUB_PROCESS;
785 MachProcess::CleanupAfterAttach(attach_token, launch_flavor, success,
786 prepare_error);
787
788 return waitfor_pid;
789 }
790
DNBProcessDetach(nub_process_t pid)791 nub_bool_t DNBProcessDetach(nub_process_t pid) {
792 MachProcessSP procSP;
793 if (GetProcessSP(pid, procSP)) {
794 const bool remove = true;
795 DNBLogThreaded(
796 "Disabling breakpoints and watchpoints, and detaching from %d.", pid);
797 procSP->DisableAllBreakpoints(remove);
798 procSP->DisableAllWatchpoints(remove);
799 return procSP->Detach();
800 }
801 return false;
802 }
803
DNBProcessKill(nub_process_t pid)804 nub_bool_t DNBProcessKill(nub_process_t pid) {
805 MachProcessSP procSP;
806 if (GetProcessSP(pid, procSP)) {
807 return procSP->Kill();
808 }
809 return false;
810 }
811
DNBProcessSignal(nub_process_t pid,int signal)812 nub_bool_t DNBProcessSignal(nub_process_t pid, int signal) {
813 MachProcessSP procSP;
814 if (GetProcessSP(pid, procSP)) {
815 return procSP->Signal(signal);
816 }
817 return false;
818 }
819
DNBProcessInterrupt(nub_process_t pid)820 nub_bool_t DNBProcessInterrupt(nub_process_t pid) {
821 MachProcessSP procSP;
822 if (GetProcessSP(pid, procSP))
823 return procSP->Interrupt();
824 return false;
825 }
826
DNBProcessSendEvent(nub_process_t pid,const char * event)827 nub_bool_t DNBProcessSendEvent(nub_process_t pid, const char *event) {
828 MachProcessSP procSP;
829 if (GetProcessSP(pid, procSP)) {
830 // FIXME: Do something with the error...
831 DNBError send_error;
832 return procSP->SendEvent(event, send_error);
833 }
834 return false;
835 }
836
DNBProcessIsAlive(nub_process_t pid)837 nub_bool_t DNBProcessIsAlive(nub_process_t pid) {
838 MachProcessSP procSP;
839 if (GetProcessSP(pid, procSP)) {
840 return MachTask::IsValid(procSP->Task().TaskPort());
841 }
842 return eStateInvalid;
843 }
844
845 // Process and Thread state information
DNBProcessGetState(nub_process_t pid)846 nub_state_t DNBProcessGetState(nub_process_t pid) {
847 MachProcessSP procSP;
848 if (GetProcessSP(pid, procSP)) {
849 return procSP->GetState();
850 }
851 return eStateInvalid;
852 }
853
854 // Process and Thread state information
DNBProcessGetExitStatus(nub_process_t pid,int * status)855 nub_bool_t DNBProcessGetExitStatus(nub_process_t pid, int *status) {
856 MachProcessSP procSP;
857 if (GetProcessSP(pid, procSP)) {
858 return procSP->GetExitStatus(status);
859 }
860 return false;
861 }
862
DNBProcessSetExitStatus(nub_process_t pid,int status)863 nub_bool_t DNBProcessSetExitStatus(nub_process_t pid, int status) {
864 MachProcessSP procSP;
865 if (GetProcessSP(pid, procSP)) {
866 procSP->SetExitStatus(status);
867 return true;
868 }
869 return false;
870 }
871
DNBProcessGetExitInfo(nub_process_t pid)872 const char *DNBProcessGetExitInfo(nub_process_t pid) {
873 MachProcessSP procSP;
874 if (GetProcessSP(pid, procSP)) {
875 return procSP->GetExitInfo();
876 }
877 return NULL;
878 }
879
DNBProcessSetExitInfo(nub_process_t pid,const char * info)880 nub_bool_t DNBProcessSetExitInfo(nub_process_t pid, const char *info) {
881 MachProcessSP procSP;
882 if (GetProcessSP(pid, procSP)) {
883 procSP->SetExitInfo(info);
884 return true;
885 }
886 return false;
887 }
888
DNBThreadGetName(nub_process_t pid,nub_thread_t tid)889 const char *DNBThreadGetName(nub_process_t pid, nub_thread_t tid) {
890 MachProcessSP procSP;
891 if (GetProcessSP(pid, procSP))
892 return procSP->ThreadGetName(tid);
893 return NULL;
894 }
895
896 nub_bool_t
DNBThreadGetIdentifierInfo(nub_process_t pid,nub_thread_t tid,thread_identifier_info_data_t * ident_info)897 DNBThreadGetIdentifierInfo(nub_process_t pid, nub_thread_t tid,
898 thread_identifier_info_data_t *ident_info) {
899 MachProcessSP procSP;
900 if (GetProcessSP(pid, procSP))
901 return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);
902 return false;
903 }
904
DNBThreadGetState(nub_process_t pid,nub_thread_t tid)905 nub_state_t DNBThreadGetState(nub_process_t pid, nub_thread_t tid) {
906 MachProcessSP procSP;
907 if (GetProcessSP(pid, procSP)) {
908 return procSP->ThreadGetState(tid);
909 }
910 return eStateInvalid;
911 }
912
DNBStateAsString(nub_state_t state)913 const char *DNBStateAsString(nub_state_t state) {
914 switch (state) {
915 case eStateInvalid:
916 return "Invalid";
917 case eStateUnloaded:
918 return "Unloaded";
919 case eStateAttaching:
920 return "Attaching";
921 case eStateLaunching:
922 return "Launching";
923 case eStateStopped:
924 return "Stopped";
925 case eStateRunning:
926 return "Running";
927 case eStateStepping:
928 return "Stepping";
929 case eStateCrashed:
930 return "Crashed";
931 case eStateDetached:
932 return "Detached";
933 case eStateExited:
934 return "Exited";
935 case eStateSuspended:
936 return "Suspended";
937 }
938 return "nub_state_t ???";
939 }
940
DNBGetGenealogyInfoForThread(nub_process_t pid,nub_thread_t tid,bool & timed_out)941 Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread(nub_process_t pid,
942 nub_thread_t tid,
943 bool &timed_out) {
944 Genealogy::ThreadActivitySP thread_activity_sp;
945 MachProcessSP procSP;
946 if (GetProcessSP(pid, procSP))
947 thread_activity_sp = procSP->GetGenealogyInfoForThread(tid, timed_out);
948 return thread_activity_sp;
949 }
950
DNBGetGenealogyImageInfo(nub_process_t pid,size_t idx)951 Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo(nub_process_t pid,
952 size_t idx) {
953 Genealogy::ProcessExecutableInfoSP image_info_sp;
954 MachProcessSP procSP;
955 if (GetProcessSP(pid, procSP)) {
956 image_info_sp = procSP->GetGenealogyImageInfo(idx);
957 }
958 return image_info_sp;
959 }
960
DNBGetRequestedQoSForThread(nub_process_t pid,nub_thread_t tid,nub_addr_t tsd,uint64_t dti_qos_class_index)961 ThreadInfo::QoS DNBGetRequestedQoSForThread(nub_process_t pid, nub_thread_t tid,
962 nub_addr_t tsd,
963 uint64_t dti_qos_class_index) {
964 MachProcessSP procSP;
965 if (GetProcessSP(pid, procSP)) {
966 return procSP->GetRequestedQoS(tid, tsd, dti_qos_class_index);
967 }
968 return ThreadInfo::QoS();
969 }
970
DNBGetPThreadT(nub_process_t pid,nub_thread_t tid)971 nub_addr_t DNBGetPThreadT(nub_process_t pid, nub_thread_t tid) {
972 MachProcessSP procSP;
973 if (GetProcessSP(pid, procSP)) {
974 return procSP->GetPThreadT(tid);
975 }
976 return INVALID_NUB_ADDRESS;
977 }
978
DNBGetDispatchQueueT(nub_process_t pid,nub_thread_t tid)979 nub_addr_t DNBGetDispatchQueueT(nub_process_t pid, nub_thread_t tid) {
980 MachProcessSP procSP;
981 if (GetProcessSP(pid, procSP)) {
982 return procSP->GetDispatchQueueT(tid);
983 }
984 return INVALID_NUB_ADDRESS;
985 }
986
987 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)988 DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
989 uint64_t plo_pthread_tsd_base_address_offset,
990 uint64_t plo_pthread_tsd_base_offset,
991 uint64_t plo_pthread_tsd_entry_size) {
992 MachProcessSP procSP;
993 if (GetProcessSP(pid, procSP)) {
994 return procSP->GetTSDAddressForThread(
995 tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
996 plo_pthread_tsd_entry_size);
997 }
998 return INVALID_NUB_ADDRESS;
999 }
1000
DNBGetLoadedDynamicLibrariesInfos(nub_process_t pid,nub_addr_t image_list_address,nub_addr_t image_count)1001 JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
1002 nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
1003 MachProcessSP procSP;
1004 if (GetProcessSP(pid, procSP)) {
1005 return procSP->GetLoadedDynamicLibrariesInfos(pid, image_list_address,
1006 image_count);
1007 }
1008 return JSONGenerator::ObjectSP();
1009 }
1010
DNBGetAllLoadedLibrariesInfos(nub_process_t pid)1011 JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid) {
1012 MachProcessSP procSP;
1013 if (GetProcessSP(pid, procSP)) {
1014 return procSP->GetAllLoadedLibrariesInfos(pid);
1015 }
1016 return JSONGenerator::ObjectSP();
1017 }
1018
1019 JSONGenerator::ObjectSP
DNBGetLibrariesInfoForAddresses(nub_process_t pid,std::vector<uint64_t> & macho_addresses)1020 DNBGetLibrariesInfoForAddresses(nub_process_t pid,
1021 std::vector<uint64_t> &macho_addresses) {
1022 MachProcessSP procSP;
1023 if (GetProcessSP(pid, procSP)) {
1024 return procSP->GetLibrariesInfoForAddresses(pid, macho_addresses);
1025 }
1026 return JSONGenerator::ObjectSP();
1027 }
1028
DNBGetSharedCacheInfo(nub_process_t pid)1029 JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid) {
1030 MachProcessSP procSP;
1031 if (GetProcessSP(pid, procSP)) {
1032 return procSP->GetSharedCacheInfo(pid);
1033 }
1034 return JSONGenerator::ObjectSP();
1035 }
1036
DNBProcessGetExecutablePath(nub_process_t pid)1037 const char *DNBProcessGetExecutablePath(nub_process_t pid) {
1038 MachProcessSP procSP;
1039 if (GetProcessSP(pid, procSP)) {
1040 return procSP->Path();
1041 }
1042 return NULL;
1043 }
1044
DNBProcessGetArgumentCount(nub_process_t pid)1045 nub_size_t DNBProcessGetArgumentCount(nub_process_t pid) {
1046 MachProcessSP procSP;
1047 if (GetProcessSP(pid, procSP)) {
1048 return procSP->ArgumentCount();
1049 }
1050 return 0;
1051 }
1052
DNBProcessGetArgumentAtIndex(nub_process_t pid,nub_size_t idx)1053 const char *DNBProcessGetArgumentAtIndex(nub_process_t pid, nub_size_t idx) {
1054 MachProcessSP procSP;
1055 if (GetProcessSP(pid, procSP)) {
1056 return procSP->ArgumentAtIndex(idx);
1057 }
1058 return NULL;
1059 }
1060
1061 // Execution control
DNBProcessResume(nub_process_t pid,const DNBThreadResumeAction * actions,size_t num_actions)1062 nub_bool_t DNBProcessResume(nub_process_t pid,
1063 const DNBThreadResumeAction *actions,
1064 size_t num_actions) {
1065 DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
1066 MachProcessSP procSP;
1067 if (GetProcessSP(pid, procSP)) {
1068 DNBThreadResumeActions thread_actions(actions, num_actions);
1069
1070 // Below we add a default thread plan just in case one wasn't
1071 // provided so all threads always know what they were supposed to do
1072 if (thread_actions.IsEmpty()) {
1073 // No thread plans were given, so the default it to run all threads
1074 thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
1075 } else {
1076 // Some thread plans were given which means anything that wasn't
1077 // specified should remain stopped.
1078 thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
1079 }
1080 return procSP->Resume(thread_actions);
1081 }
1082 return false;
1083 }
1084
DNBProcessHalt(nub_process_t pid)1085 nub_bool_t DNBProcessHalt(nub_process_t pid) {
1086 DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
1087 MachProcessSP procSP;
1088 if (GetProcessSP(pid, procSP))
1089 return procSP->Signal(SIGSTOP);
1090 return false;
1091 }
1092 //
1093 // nub_bool_t
1094 // DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)
1095 //{
1096 // DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)",
1097 // __FUNCTION__, pid, tid, (uint32_t)step);
1098 // MachProcessSP procSP;
1099 // if (GetProcessSP (pid, procSP))
1100 // {
1101 // return procSP->Resume(tid, step, 0);
1102 // }
1103 // return false;
1104 //}
1105 //
1106 // nub_bool_t
1107 // DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t
1108 // step, int signal)
1109 //{
1110 // DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u,
1111 // signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);
1112 // MachProcessSP procSP;
1113 // if (GetProcessSP (pid, procSP))
1114 // {
1115 // return procSP->Resume(tid, step, signal);
1116 // }
1117 // return false;
1118 //}
1119
DNBProcessWaitForEvents(nub_process_t pid,nub_event_t event_mask,bool wait_for_set,struct timespec * timeout)1120 nub_event_t DNBProcessWaitForEvents(nub_process_t pid, nub_event_t event_mask,
1121 bool wait_for_set,
1122 struct timespec *timeout) {
1123 nub_event_t result = 0;
1124 MachProcessSP procSP;
1125 if (GetProcessSP(pid, procSP)) {
1126 if (wait_for_set)
1127 result = procSP->Events().WaitForSetEvents(event_mask, timeout);
1128 else
1129 result = procSP->Events().WaitForEventsToReset(event_mask, timeout);
1130 }
1131 return result;
1132 }
1133
DNBProcessResetEvents(nub_process_t pid,nub_event_t event_mask)1134 void DNBProcessResetEvents(nub_process_t pid, nub_event_t event_mask) {
1135 MachProcessSP procSP;
1136 if (GetProcessSP(pid, procSP))
1137 procSP->Events().ResetEvents(event_mask);
1138 }
1139
1140 // Breakpoints
DNBBreakpointSet(nub_process_t pid,nub_addr_t addr,nub_size_t size,nub_bool_t hardware)1141 nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
1142 nub_bool_t hardware) {
1143 MachProcessSP procSP;
1144 if (GetProcessSP(pid, procSP))
1145 return procSP->CreateBreakpoint(addr, size, hardware) != NULL;
1146 return false;
1147 }
1148
DNBBreakpointClear(nub_process_t pid,nub_addr_t addr)1149 nub_bool_t DNBBreakpointClear(nub_process_t pid, nub_addr_t addr) {
1150 MachProcessSP procSP;
1151 if (GetProcessSP(pid, procSP))
1152 return procSP->DisableBreakpoint(addr, true);
1153 return false; // Failed
1154 }
1155
1156 // Watchpoints
DNBWatchpointSet(nub_process_t pid,nub_addr_t addr,nub_size_t size,uint32_t watch_flags,nub_bool_t hardware)1157 nub_bool_t DNBWatchpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
1158 uint32_t watch_flags, nub_bool_t hardware) {
1159 MachProcessSP procSP;
1160 if (GetProcessSP(pid, procSP))
1161 return procSP->CreateWatchpoint(addr, size, watch_flags, hardware) != NULL;
1162 return false;
1163 }
1164
DNBWatchpointClear(nub_process_t pid,nub_addr_t addr)1165 nub_bool_t DNBWatchpointClear(nub_process_t pid, nub_addr_t addr) {
1166 MachProcessSP procSP;
1167 if (GetProcessSP(pid, procSP))
1168 return procSP->DisableWatchpoint(addr, true);
1169 return false; // Failed
1170 }
1171
1172 // Return the number of supported hardware watchpoints.
DNBWatchpointGetNumSupportedHWP(nub_process_t pid)1173 uint32_t DNBWatchpointGetNumSupportedHWP(nub_process_t pid) {
1174 MachProcessSP procSP;
1175 if (GetProcessSP(pid, procSP))
1176 return procSP->GetNumSupportedHardwareWatchpoints();
1177 return 0;
1178 }
1179
1180 // Read memory in the address space of process PID. This call will take
1181 // care of setting and restoring permissions and breaking up the memory
1182 // read into multiple chunks as required.
1183 //
1184 // RETURNS: number of bytes actually read
DNBProcessMemoryRead(nub_process_t pid,nub_addr_t addr,nub_size_t size,void * buf)1185 nub_size_t DNBProcessMemoryRead(nub_process_t pid, nub_addr_t addr,
1186 nub_size_t size, void *buf) {
1187 MachProcessSP procSP;
1188 if (GetProcessSP(pid, procSP))
1189 return procSP->ReadMemory(addr, size, buf);
1190 return 0;
1191 }
1192
DNBProcessMemoryReadInteger(nub_process_t pid,nub_addr_t addr,nub_size_t integer_size,uint64_t fail_value)1193 uint64_t DNBProcessMemoryReadInteger(nub_process_t pid, nub_addr_t addr,
1194 nub_size_t integer_size,
1195 uint64_t fail_value) {
1196 union Integers {
1197 uint8_t u8;
1198 uint16_t u16;
1199 uint32_t u32;
1200 uint64_t u64;
1201 };
1202
1203 if (integer_size <= sizeof(uint64_t)) {
1204 Integers ints;
1205 if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) {
1206 switch (integer_size) {
1207 case 1:
1208 return ints.u8;
1209 case 2:
1210 return ints.u16;
1211 case 3:
1212 return ints.u32 & 0xffffffu;
1213 case 4:
1214 return ints.u32;
1215 case 5:
1216 return ints.u32 & 0x000000ffffffffffull;
1217 case 6:
1218 return ints.u32 & 0x0000ffffffffffffull;
1219 case 7:
1220 return ints.u32 & 0x00ffffffffffffffull;
1221 case 8:
1222 return ints.u64;
1223 }
1224 }
1225 }
1226 return fail_value;
1227 }
1228
DNBProcessMemoryReadPointer(nub_process_t pid,nub_addr_t addr)1229 nub_addr_t DNBProcessMemoryReadPointer(nub_process_t pid, nub_addr_t addr) {
1230 cpu_type_t cputype = DNBProcessGetCPUType(pid);
1231 if (cputype) {
1232 const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4;
1233 return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0);
1234 }
1235 return 0;
1236 }
1237
DNBProcessMemoryReadCString(nub_process_t pid,nub_addr_t addr)1238 std::string DNBProcessMemoryReadCString(nub_process_t pid, nub_addr_t addr) {
1239 std::string cstr;
1240 char buffer[256];
1241 const nub_size_t max_buffer_cstr_length = sizeof(buffer) - 1;
1242 buffer[max_buffer_cstr_length] = '\0';
1243 nub_size_t length = 0;
1244 nub_addr_t curr_addr = addr;
1245 do {
1246 nub_size_t bytes_read =
1247 DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer);
1248 if (bytes_read == 0)
1249 break;
1250 length = strlen(buffer);
1251 cstr.append(buffer, length);
1252 curr_addr += length;
1253 } while (length == max_buffer_cstr_length);
1254 return cstr;
1255 }
1256
DNBProcessMemoryReadCStringFixed(nub_process_t pid,nub_addr_t addr,nub_size_t fixed_length)1257 std::string DNBProcessMemoryReadCStringFixed(nub_process_t pid, nub_addr_t addr,
1258 nub_size_t fixed_length) {
1259 std::string cstr;
1260 char buffer[fixed_length + 1];
1261 buffer[fixed_length] = '\0';
1262 nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer);
1263 if (bytes_read > 0)
1264 cstr.assign(buffer);
1265 return cstr;
1266 }
1267
1268 // Write memory to the address space of process PID. This call will take
1269 // care of setting and restoring permissions and breaking up the memory
1270 // write into multiple chunks as required.
1271 //
1272 // RETURNS: number of bytes actually written
DNBProcessMemoryWrite(nub_process_t pid,nub_addr_t addr,nub_size_t size,const void * buf)1273 nub_size_t DNBProcessMemoryWrite(nub_process_t pid, nub_addr_t addr,
1274 nub_size_t size, const void *buf) {
1275 MachProcessSP procSP;
1276 if (GetProcessSP(pid, procSP))
1277 return procSP->WriteMemory(addr, size, buf);
1278 return 0;
1279 }
1280
DNBProcessMemoryAllocate(nub_process_t pid,nub_size_t size,uint32_t permissions)1281 nub_addr_t DNBProcessMemoryAllocate(nub_process_t pid, nub_size_t size,
1282 uint32_t permissions) {
1283 MachProcessSP procSP;
1284 if (GetProcessSP(pid, procSP))
1285 return procSP->Task().AllocateMemory(size, permissions);
1286 return 0;
1287 }
1288
DNBProcessMemoryDeallocate(nub_process_t pid,nub_addr_t addr)1289 nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid, nub_addr_t addr) {
1290 MachProcessSP procSP;
1291 if (GetProcessSP(pid, procSP))
1292 return procSP->Task().DeallocateMemory(addr);
1293 return 0;
1294 }
1295
1296 // Find attributes of the memory region that contains ADDR for process PID,
1297 // if possible, and return a string describing those attributes.
1298 //
1299 // Returns 1 if we could find attributes for this region and OUTBUF can
1300 // be sent to the remote debugger.
1301 //
1302 // Returns 0 if we couldn't find the attributes for a region of memory at
1303 // that address and OUTBUF should not be sent.
1304 //
1305 // Returns -1 if this platform cannot look up information about memory regions
1306 // or if we do not yet have a valid launched process.
1307 //
DNBProcessMemoryRegionInfo(nub_process_t pid,nub_addr_t addr,DNBRegionInfo * region_info)1308 int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
1309 DNBRegionInfo *region_info) {
1310 MachProcessSP procSP;
1311 if (GetProcessSP(pid, procSP))
1312 return procSP->Task().GetMemoryRegionInfo(addr, region_info);
1313
1314 return -1;
1315 }
1316
DNBProcessGetProfileData(nub_process_t pid,DNBProfileDataScanType scanType)1317 std::string DNBProcessGetProfileData(nub_process_t pid,
1318 DNBProfileDataScanType scanType) {
1319 MachProcessSP procSP;
1320 if (GetProcessSP(pid, procSP))
1321 return procSP->Task().GetProfileData(scanType);
1322
1323 return std::string("");
1324 }
1325
DNBProcessSetEnableAsyncProfiling(nub_process_t pid,nub_bool_t enable,uint64_t interval_usec,DNBProfileDataScanType scan_type)1326 nub_bool_t DNBProcessSetEnableAsyncProfiling(nub_process_t pid,
1327 nub_bool_t enable,
1328 uint64_t interval_usec,
1329 DNBProfileDataScanType scan_type) {
1330 MachProcessSP procSP;
1331 if (GetProcessSP(pid, procSP)) {
1332 procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);
1333 return true;
1334 }
1335
1336 return false;
1337 }
1338
1339 // Get the number of threads for the specified process.
DNBProcessGetNumThreads(nub_process_t pid)1340 nub_size_t DNBProcessGetNumThreads(nub_process_t pid) {
1341 MachProcessSP procSP;
1342 if (GetProcessSP(pid, procSP))
1343 return procSP->GetNumThreads();
1344 return 0;
1345 }
1346
1347 // Get the thread ID of the current thread.
DNBProcessGetCurrentThread(nub_process_t pid)1348 nub_thread_t DNBProcessGetCurrentThread(nub_process_t pid) {
1349 MachProcessSP procSP;
1350 if (GetProcessSP(pid, procSP))
1351 return procSP->GetCurrentThread();
1352 return 0;
1353 }
1354
1355 // Get the mach port number of the current thread.
DNBProcessGetCurrentThreadMachPort(nub_process_t pid)1356 nub_thread_t DNBProcessGetCurrentThreadMachPort(nub_process_t pid) {
1357 MachProcessSP procSP;
1358 if (GetProcessSP(pid, procSP))
1359 return procSP->GetCurrentThreadMachPort();
1360 return 0;
1361 }
1362
1363 // Change the current thread.
DNBProcessSetCurrentThread(nub_process_t pid,nub_thread_t tid)1364 nub_thread_t DNBProcessSetCurrentThread(nub_process_t pid, nub_thread_t tid) {
1365 MachProcessSP procSP;
1366 if (GetProcessSP(pid, procSP))
1367 return procSP->SetCurrentThread(tid);
1368 return INVALID_NUB_THREAD;
1369 }
1370
1371 // Dump a string describing a thread's stop reason to the specified file
1372 // handle
DNBThreadGetStopReason(nub_process_t pid,nub_thread_t tid,struct DNBThreadStopInfo * stop_info)1373 nub_bool_t DNBThreadGetStopReason(nub_process_t pid, nub_thread_t tid,
1374 struct DNBThreadStopInfo *stop_info) {
1375 MachProcessSP procSP;
1376 if (GetProcessSP(pid, procSP))
1377 return procSP->GetThreadStoppedReason(tid, stop_info);
1378 return false;
1379 }
1380
1381 // Return string description for the specified thread.
1382 //
1383 // RETURNS: NULL if the thread isn't valid, else a NULL terminated C
1384 // string from a static buffer that must be copied prior to subsequent
1385 // calls.
DNBThreadGetInfo(nub_process_t pid,nub_thread_t tid)1386 const char *DNBThreadGetInfo(nub_process_t pid, nub_thread_t tid) {
1387 MachProcessSP procSP;
1388 if (GetProcessSP(pid, procSP))
1389 return procSP->GetThreadInfo(tid);
1390 return NULL;
1391 }
1392
1393 // Get the thread ID given a thread index.
DNBProcessGetThreadAtIndex(nub_process_t pid,size_t thread_idx)1394 nub_thread_t DNBProcessGetThreadAtIndex(nub_process_t pid, size_t thread_idx) {
1395 MachProcessSP procSP;
1396 if (GetProcessSP(pid, procSP))
1397 return procSP->GetThreadAtIndex(thread_idx);
1398 return INVALID_NUB_THREAD;
1399 }
1400
1401 // Do whatever is needed to sync the thread's register state with it's kernel
1402 // values.
DNBProcessSyncThreadState(nub_process_t pid,nub_thread_t tid)1403 nub_bool_t DNBProcessSyncThreadState(nub_process_t pid, nub_thread_t tid) {
1404 MachProcessSP procSP;
1405 if (GetProcessSP(pid, procSP))
1406 return procSP->SyncThreadState(tid);
1407 return false;
1408 }
1409
DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid)1410 nub_addr_t DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid) {
1411 MachProcessSP procSP;
1412 DNBError err;
1413 if (GetProcessSP(pid, procSP))
1414 return procSP->Task().GetDYLDAllImageInfosAddress(err);
1415 return INVALID_NUB_ADDRESS;
1416 }
1417
DNBProcessSharedLibrariesUpdated(nub_process_t pid)1418 nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) {
1419 MachProcessSP procSP;
1420 if (GetProcessSP(pid, procSP)) {
1421 procSP->SharedLibrariesUpdated();
1422 return true;
1423 }
1424 return false;
1425 }
1426
DNBGetDeploymentInfo(nub_process_t pid,bool is_executable,const struct load_command & lc,uint64_t load_command_address,uint32_t & major_version,uint32_t & minor_version,uint32_t & patch_version)1427 const char *DNBGetDeploymentInfo(nub_process_t pid, bool is_executable,
1428 const struct load_command &lc,
1429 uint64_t load_command_address,
1430 uint32_t &major_version,
1431 uint32_t &minor_version,
1432 uint32_t &patch_version) {
1433 MachProcessSP procSP;
1434 if (GetProcessSP(pid, procSP)) {
1435 // FIXME: This doesn't return the correct result when xctest (a
1436 // macOS binary) is loaded with the macCatalyst dyld platform
1437 // override. The image info corrects for this, but qProcessInfo
1438 // will return what is in the binary.
1439 auto info =
1440 procSP->GetDeploymentInfo(lc, load_command_address, is_executable);
1441 major_version = info.major_version;
1442 minor_version = info.minor_version;
1443 patch_version = info.patch_version;
1444 return procSP->GetPlatformString(info.platform);
1445 }
1446 return nullptr;
1447 }
1448
1449 // Get the current shared library information for a process. Only return
1450 // the shared libraries that have changed since the last shared library
1451 // state changed event if only_changed is non-zero.
1452 nub_size_t
DNBProcessGetSharedLibraryInfo(nub_process_t pid,nub_bool_t only_changed,struct DNBExecutableImageInfo ** image_infos)1453 DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,
1454 struct DNBExecutableImageInfo **image_infos) {
1455 MachProcessSP procSP;
1456 if (GetProcessSP(pid, procSP))
1457 return procSP->CopyImageInfos(image_infos, only_changed);
1458
1459 // If we have no process, then return NULL for the shared library info
1460 // and zero for shared library count
1461 *image_infos = NULL;
1462 return 0;
1463 }
1464
DNBGetRegisterCPUType()1465 uint32_t DNBGetRegisterCPUType() {
1466 return DNBArchProtocol::GetRegisterCPUType();
1467 }
1468 // Get the register set information for a specific thread.
DNBGetRegisterSetInfo(nub_size_t * num_reg_sets)1469 const DNBRegisterSetInfo *DNBGetRegisterSetInfo(nub_size_t *num_reg_sets) {
1470 return DNBArchProtocol::GetRegisterSetInfo(num_reg_sets);
1471 }
1472
1473 // 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)1474 nub_bool_t DNBThreadGetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
1475 uint32_t set, uint32_t reg,
1476 DNBRegisterValue *value) {
1477 MachProcessSP procSP;
1478 ::bzero(value, sizeof(DNBRegisterValue));
1479 if (GetProcessSP(pid, procSP)) {
1480 if (tid != INVALID_NUB_THREAD)
1481 return procSP->GetRegisterValue(tid, set, reg, value);
1482 }
1483 return false;
1484 }
1485
DNBThreadSetRegisterValueByID(nub_process_t pid,nub_thread_t tid,uint32_t set,uint32_t reg,const DNBRegisterValue * value)1486 nub_bool_t DNBThreadSetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
1487 uint32_t set, uint32_t reg,
1488 const DNBRegisterValue *value) {
1489 if (tid != INVALID_NUB_THREAD) {
1490 MachProcessSP procSP;
1491 if (GetProcessSP(pid, procSP))
1492 return procSP->SetRegisterValue(tid, set, reg, value);
1493 }
1494 return false;
1495 }
1496
DNBThreadGetRegisterContext(nub_process_t pid,nub_thread_t tid,void * buf,size_t buf_len)1497 nub_size_t DNBThreadGetRegisterContext(nub_process_t pid, nub_thread_t tid,
1498 void *buf, size_t buf_len) {
1499 MachProcessSP procSP;
1500 if (GetProcessSP(pid, procSP)) {
1501 if (tid != INVALID_NUB_THREAD)
1502 return procSP->GetThreadList().GetRegisterContext(tid, buf, buf_len);
1503 }
1504 ::bzero(buf, buf_len);
1505 return 0;
1506 }
1507
DNBThreadSetRegisterContext(nub_process_t pid,nub_thread_t tid,const void * buf,size_t buf_len)1508 nub_size_t DNBThreadSetRegisterContext(nub_process_t pid, nub_thread_t tid,
1509 const void *buf, size_t buf_len) {
1510 MachProcessSP procSP;
1511 if (GetProcessSP(pid, procSP)) {
1512 if (tid != INVALID_NUB_THREAD)
1513 return procSP->GetThreadList().SetRegisterContext(tid, buf, buf_len);
1514 }
1515 return 0;
1516 }
1517
DNBThreadSaveRegisterState(nub_process_t pid,nub_thread_t tid)1518 uint32_t DNBThreadSaveRegisterState(nub_process_t pid, nub_thread_t tid) {
1519 if (tid != INVALID_NUB_THREAD) {
1520 MachProcessSP procSP;
1521 if (GetProcessSP(pid, procSP))
1522 return procSP->GetThreadList().SaveRegisterState(tid);
1523 }
1524 return 0;
1525 }
DNBThreadRestoreRegisterState(nub_process_t pid,nub_thread_t tid,uint32_t save_id)1526 nub_bool_t DNBThreadRestoreRegisterState(nub_process_t pid, nub_thread_t tid,
1527 uint32_t save_id) {
1528 if (tid != INVALID_NUB_THREAD) {
1529 MachProcessSP procSP;
1530 if (GetProcessSP(pid, procSP))
1531 return procSP->GetThreadList().RestoreRegisterState(tid, save_id);
1532 }
1533 return false;
1534 }
1535
1536 // 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)1537 nub_bool_t DNBThreadGetRegisterValueByName(nub_process_t pid, nub_thread_t tid,
1538 uint32_t reg_set,
1539 const char *reg_name,
1540 DNBRegisterValue *value) {
1541 MachProcessSP procSP;
1542 ::bzero(value, sizeof(DNBRegisterValue));
1543 if (GetProcessSP(pid, procSP)) {
1544 const struct DNBRegisterSetInfo *set_info;
1545 nub_size_t num_reg_sets = 0;
1546 set_info = DNBGetRegisterSetInfo(&num_reg_sets);
1547 if (set_info) {
1548 uint32_t set = reg_set;
1549 uint32_t reg;
1550 if (set == REGISTER_SET_ALL) {
1551 for (set = 1; set < num_reg_sets; ++set) {
1552 for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1553 if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1554 return procSP->GetRegisterValue(tid, set, reg, value);
1555 }
1556 }
1557 } else {
1558 for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1559 if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1560 return procSP->GetRegisterValue(tid, set, reg, value);
1561 }
1562 }
1563 }
1564 }
1565 return false;
1566 }
1567
1568 // Read a register set and register number from the register name.
DNBGetRegisterInfoByName(const char * reg_name,DNBRegisterInfo * info)1569 nub_bool_t DNBGetRegisterInfoByName(const char *reg_name,
1570 DNBRegisterInfo *info) {
1571 const struct DNBRegisterSetInfo *set_info;
1572 nub_size_t num_reg_sets = 0;
1573 set_info = DNBGetRegisterSetInfo(&num_reg_sets);
1574 if (set_info) {
1575 uint32_t set, reg;
1576 for (set = 1; set < num_reg_sets; ++set) {
1577 for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1578 if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) {
1579 *info = set_info[set].registers[reg];
1580 return true;
1581 }
1582 }
1583 }
1584
1585 for (set = 1; set < num_reg_sets; ++set) {
1586 uint32_t reg;
1587 for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1588 if (set_info[set].registers[reg].alt == NULL)
1589 continue;
1590
1591 if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0) {
1592 *info = set_info[set].registers[reg];
1593 return true;
1594 }
1595 }
1596 }
1597 }
1598
1599 ::bzero(info, sizeof(DNBRegisterInfo));
1600 return false;
1601 }
1602
1603 // Set the name to address callback function that this nub can use
1604 // for any name to address lookups that are needed.
DNBProcessSetNameToAddressCallback(nub_process_t pid,DNBCallbackNameToAddress callback,void * baton)1605 nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,
1606 DNBCallbackNameToAddress callback,
1607 void *baton) {
1608 MachProcessSP procSP;
1609 if (GetProcessSP(pid, procSP)) {
1610 procSP->SetNameToAddressCallback(callback, baton);
1611 return true;
1612 }
1613 return false;
1614 }
1615
1616 // Set the name to address callback function that this nub can use
1617 // for any name to address lookups that are needed.
DNBProcessSetSharedLibraryInfoCallback(nub_process_t pid,DNBCallbackCopyExecutableImageInfos callback,void * baton)1618 nub_bool_t DNBProcessSetSharedLibraryInfoCallback(
1619 nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback,
1620 void *baton) {
1621 MachProcessSP procSP;
1622 if (GetProcessSP(pid, procSP)) {
1623 procSP->SetSharedLibraryInfoCallback(callback, baton);
1624 return true;
1625 }
1626 return false;
1627 }
1628
DNBProcessLookupAddress(nub_process_t pid,const char * name,const char * shlib)1629 nub_addr_t DNBProcessLookupAddress(nub_process_t pid, const char *name,
1630 const char *shlib) {
1631 MachProcessSP procSP;
1632 if (GetProcessSP(pid, procSP)) {
1633 return procSP->LookupSymbol(name, shlib);
1634 }
1635 return INVALID_NUB_ADDRESS;
1636 }
1637
DNBProcessGetAvailableSTDOUT(nub_process_t pid,char * buf,nub_size_t buf_size)1638 nub_size_t DNBProcessGetAvailableSTDOUT(nub_process_t pid, char *buf,
1639 nub_size_t buf_size) {
1640 MachProcessSP procSP;
1641 if (GetProcessSP(pid, procSP))
1642 return procSP->GetAvailableSTDOUT(buf, buf_size);
1643 return 0;
1644 }
1645
DNBProcessGetAvailableSTDERR(nub_process_t pid,char * buf,nub_size_t buf_size)1646 nub_size_t DNBProcessGetAvailableSTDERR(nub_process_t pid, char *buf,
1647 nub_size_t buf_size) {
1648 MachProcessSP procSP;
1649 if (GetProcessSP(pid, procSP))
1650 return procSP->GetAvailableSTDERR(buf, buf_size);
1651 return 0;
1652 }
1653
DNBProcessGetAvailableProfileData(nub_process_t pid,char * buf,nub_size_t buf_size)1654 nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,
1655 nub_size_t buf_size) {
1656 MachProcessSP procSP;
1657 if (GetProcessSP(pid, procSP))
1658 return procSP->GetAsyncProfileData(buf, buf_size);
1659 return 0;
1660 }
1661
DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid)1662 DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid) {
1663 return DarwinLogCollector::GetEventsForProcess(pid);
1664 }
1665
DNBProcessGetStopCount(nub_process_t pid)1666 nub_size_t DNBProcessGetStopCount(nub_process_t pid) {
1667 MachProcessSP procSP;
1668 if (GetProcessSP(pid, procSP))
1669 return procSP->StopCount();
1670 return 0;
1671 }
1672
DNBProcessGetCPUType(nub_process_t pid)1673 uint32_t DNBProcessGetCPUType(nub_process_t pid) {
1674 MachProcessSP procSP;
1675 if (GetProcessSP(pid, procSP))
1676 return procSP->GetCPUType();
1677 return 0;
1678 }
1679
DNBResolveExecutablePath(const char * path,char * resolved_path,size_t resolved_path_size)1680 nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path,
1681 size_t resolved_path_size) {
1682 if (path == NULL || path[0] == '\0')
1683 return false;
1684
1685 char max_path[PATH_MAX];
1686 std::string result;
1687 CFString::GlobPath(path, result);
1688
1689 if (result.empty())
1690 result = path;
1691
1692 struct stat path_stat;
1693 if (::stat(path, &path_stat) == 0) {
1694 if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
1695 CFBundle bundle(path);
1696 CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
1697 if (url.get()) {
1698 if (::CFURLGetFileSystemRepresentation(
1699 url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
1700 return true;
1701 }
1702 }
1703 }
1704
1705 if (realpath(path, max_path)) {
1706 // Found the path relatively...
1707 ::strlcpy(resolved_path, max_path, resolved_path_size);
1708 return strlen(resolved_path) + 1 < resolved_path_size;
1709 } else {
1710 // Not a relative path, check the PATH environment variable if the
1711 const char *PATH = getenv("PATH");
1712 if (PATH) {
1713 const char *curr_path_start = PATH;
1714 const char *curr_path_end;
1715 while (curr_path_start && *curr_path_start) {
1716 curr_path_end = strchr(curr_path_start, ':');
1717 if (curr_path_end == NULL) {
1718 result.assign(curr_path_start);
1719 curr_path_start = NULL;
1720 } else if (curr_path_end > curr_path_start) {
1721 size_t len = curr_path_end - curr_path_start;
1722 result.assign(curr_path_start, len);
1723 curr_path_start += len + 1;
1724 } else
1725 break;
1726
1727 result += '/';
1728 result += path;
1729 struct stat s;
1730 if (stat(result.c_str(), &s) == 0) {
1731 ::strlcpy(resolved_path, result.c_str(), resolved_path_size);
1732 return result.size() + 1 < resolved_path_size;
1733 }
1734 }
1735 }
1736 }
1737 return false;
1738 }
1739
DNBGetOSVersionNumbers(uint64_t * major,uint64_t * minor,uint64_t * patch)1740 bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch) {
1741 return MachProcess::GetOSVersionNumbers(major, minor, patch);
1742 }
1743
DNBGetMacCatalystVersionString()1744 std::string DNBGetMacCatalystVersionString() {
1745 return MachProcess::GetMacCatalystVersionString();
1746 }
1747
DNBInitialize()1748 void DNBInitialize() {
1749 DNBLogThreadedIf(LOG_PROCESS, "DNBInitialize ()");
1750 #if defined(__i386__) || defined(__x86_64__)
1751 DNBArchImplI386::Initialize();
1752 DNBArchImplX86_64::Initialize();
1753 #elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
1754 DNBArchMachARM::Initialize();
1755 DNBArchMachARM64::Initialize();
1756 #endif
1757 }
1758
DNBTerminate()1759 void DNBTerminate() {}
1760
DNBSetArchitecture(const char * arch)1761 nub_bool_t DNBSetArchitecture(const char *arch) {
1762 if (arch && arch[0]) {
1763 if (strcasecmp(arch, "i386") == 0)
1764 return DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
1765 else if (strcasecmp(arch, "x86_64") == 0)
1766 return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64,
1767 CPU_SUBTYPE_X86_64_ALL);
1768 else if (strcasecmp(arch, "x86_64h") == 0)
1769 return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64,
1770 CPU_SUBTYPE_X86_64_H);
1771 else if (strstr(arch, "arm64_32") == arch ||
1772 strstr(arch, "aarch64_32") == arch)
1773 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32);
1774 else if (strstr(arch, "arm64e") == arch)
1775 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,
1776 CPU_SUBTYPE_ARM64E);
1777 else if (strstr(arch, "arm64") == arch || strstr(arch, "aarch64") == arch)
1778 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,
1779 CPU_SUBTYPE_ARM64_ALL);
1780 else if (strstr(arch, "armv8") == arch)
1781 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,
1782 CPU_SUBTYPE_ARM64_V8);
1783 else if (strstr(arch, "armv7em") == arch)
1784 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1785 CPU_SUBTYPE_ARM_V7EM);
1786 else if (strstr(arch, "armv7m") == arch)
1787 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1788 CPU_SUBTYPE_ARM_V7M);
1789 else if (strstr(arch, "armv7k") == arch)
1790 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1791 CPU_SUBTYPE_ARM_V7K);
1792 else if (strstr(arch, "armv7s") == arch)
1793 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1794 CPU_SUBTYPE_ARM_V7S);
1795 else if (strstr(arch, "armv7") == arch)
1796 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7);
1797 else if (strstr(arch, "armv6m") == arch)
1798 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1799 CPU_SUBTYPE_ARM_V6M);
1800 else if (strstr(arch, "armv6") == arch)
1801 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6);
1802 else if (strstr(arch, "armv5") == arch)
1803 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1804 CPU_SUBTYPE_ARM_V5TEJ);
1805 else if (strstr(arch, "armv4t") == arch)
1806 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1807 CPU_SUBTYPE_ARM_V4T);
1808 else if (strstr(arch, "arm") == arch)
1809 return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1810 CPU_SUBTYPE_ARM_ALL);
1811 }
1812 return false;
1813 }
1814