xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Greg Clayton on 12/12/07.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "RNBRemote.h"
14061da546Spatrick 
15dda28197Spatrick #include <bsm/audit.h>
16dda28197Spatrick #include <bsm/audit_session.h>
17be691f3bSpatrick #include <cerrno>
18be691f3bSpatrick #include <csignal>
19dda28197Spatrick #include <libproc.h>
20061da546Spatrick #include <mach-o/loader.h>
21061da546Spatrick #include <mach/exception_types.h>
22be691f3bSpatrick #include <mach/mach_vm.h>
23dda28197Spatrick #include <mach/task_info.h>
24dda28197Spatrick #include <pwd.h>
25061da546Spatrick #include <sys/stat.h>
26061da546Spatrick #include <sys/sysctl.h>
27061da546Spatrick #include <unistd.h>
28061da546Spatrick 
29061da546Spatrick #if defined(__APPLE__)
30061da546Spatrick #include <pthread.h>
31061da546Spatrick #include <sched.h>
32061da546Spatrick #endif
33061da546Spatrick 
34061da546Spatrick #include "DNB.h"
35061da546Spatrick #include "DNBDataRef.h"
36061da546Spatrick #include "DNBLog.h"
37061da546Spatrick #include "DNBThreadResumeActions.h"
38061da546Spatrick #include "JSON.h"
39061da546Spatrick #include "JSONGenerator.h"
40061da546Spatrick #include "JSONGenerator.h"
41061da546Spatrick #include "MacOSX/Genealogy.h"
42061da546Spatrick #include "OsLogger.h"
43061da546Spatrick #include "RNBContext.h"
44061da546Spatrick #include "RNBServices.h"
45061da546Spatrick #include "RNBSocket.h"
46061da546Spatrick #include "StdStringExtractor.h"
47061da546Spatrick 
48061da546Spatrick #include <compression.h>
49061da546Spatrick 
50061da546Spatrick #include <TargetConditionals.h>
51*f6aab3d8Srobert #include <algorithm>
52061da546Spatrick #include <iomanip>
53061da546Spatrick #include <memory>
54061da546Spatrick #include <sstream>
55061da546Spatrick #include <unordered_set>
56061da546Spatrick 
57dda28197Spatrick #include <CoreFoundation/CoreFoundation.h>
58dda28197Spatrick #include <Security/Security.h>
59dda28197Spatrick 
60061da546Spatrick // constants
61061da546Spatrick 
62061da546Spatrick static const std::string OS_LOG_EVENTS_KEY_NAME("events");
63061da546Spatrick static const std::string JSON_ASYNC_TYPE_KEY_NAME("type");
64061da546Spatrick 
65061da546Spatrick // std::iostream formatting macros
66061da546Spatrick #define RAW_HEXBASE std::setfill('0') << std::hex << std::right
67061da546Spatrick #define HEXBASE '0' << 'x' << RAW_HEXBASE
68061da546Spatrick #define RAWHEX8(x) RAW_HEXBASE << std::setw(2) << ((uint32_t)((uint8_t)x))
69061da546Spatrick #define RAWHEX16 RAW_HEXBASE << std::setw(4)
70061da546Spatrick #define RAWHEX32 RAW_HEXBASE << std::setw(8)
71061da546Spatrick #define RAWHEX64 RAW_HEXBASE << std::setw(16)
72061da546Spatrick #define HEX8(x) HEXBASE << std::setw(2) << ((uint32_t)(x))
73061da546Spatrick #define HEX16 HEXBASE << std::setw(4)
74061da546Spatrick #define HEX32 HEXBASE << std::setw(8)
75061da546Spatrick #define HEX64 HEXBASE << std::setw(16)
76061da546Spatrick #define RAW_HEX(x) RAW_HEXBASE << std::setw(sizeof(x) * 2) << (x)
77061da546Spatrick #define HEX(x) HEXBASE << std::setw(sizeof(x) * 2) << (x)
78061da546Spatrick #define RAWHEX_SIZE(x, sz) RAW_HEXBASE << std::setw((sz)) << (x)
79061da546Spatrick #define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x)
80061da546Spatrick #define STRING_WIDTH(w) std::setfill(' ') << std::setw(w)
81061da546Spatrick #define LEFT_STRING_WIDTH(s, w)                                                \
82061da546Spatrick   std::left << std::setfill(' ') << std::setw(w) << (s) << std::right
83061da546Spatrick #define DECIMAL std::dec << std::setfill(' ')
84061da546Spatrick #define DECIMAL_WIDTH(w) DECIMAL << std::setw(w)
85061da546Spatrick #define FLOAT(n, d)                                                            \
86061da546Spatrick   std::setfill(' ') << std::setw((n) + (d) + 1) << std::setprecision(d)        \
87061da546Spatrick                     << std::showpoint << std::fixed
88061da546Spatrick #define INDENT_WITH_SPACES(iword_idx)                                          \
89061da546Spatrick   std::setfill(' ') << std::setw((iword_idx)) << ""
90061da546Spatrick #define INDENT_WITH_TABS(iword_idx)                                            \
91061da546Spatrick   std::setfill('\t') << std::setw((iword_idx)) << ""
92061da546Spatrick // Class to handle communications via gdb remote protocol.
93061da546Spatrick 
94061da546Spatrick // Prototypes
95061da546Spatrick 
96061da546Spatrick static std::string binary_encode_string(const std::string &s);
97061da546Spatrick 
98061da546Spatrick // Decode a single hex character and return the hex value as a number or
99061da546Spatrick // -1 if "ch" is not a hex character.
xdigit_to_sint(char ch)100061da546Spatrick static inline int xdigit_to_sint(char ch) {
101061da546Spatrick   if (ch >= 'a' && ch <= 'f')
102061da546Spatrick     return 10 + ch - 'a';
103061da546Spatrick   if (ch >= 'A' && ch <= 'F')
104061da546Spatrick     return 10 + ch - 'A';
105061da546Spatrick   if (ch >= '0' && ch <= '9')
106061da546Spatrick     return ch - '0';
107061da546Spatrick   return -1;
108061da546Spatrick }
109061da546Spatrick 
110061da546Spatrick // Decode a single hex ASCII byte. Return -1 on failure, a value 0-255
111061da546Spatrick // on success.
decoded_hex_ascii_char(const char * p)112061da546Spatrick static inline int decoded_hex_ascii_char(const char *p) {
113061da546Spatrick   const int hi_nibble = xdigit_to_sint(p[0]);
114061da546Spatrick   if (hi_nibble == -1)
115061da546Spatrick     return -1;
116061da546Spatrick   const int lo_nibble = xdigit_to_sint(p[1]);
117061da546Spatrick   if (lo_nibble == -1)
118061da546Spatrick     return -1;
119061da546Spatrick   return (uint8_t)((hi_nibble << 4) + lo_nibble);
120061da546Spatrick }
121061da546Spatrick 
122061da546Spatrick // Decode a hex ASCII string back into a string
decode_hex_ascii_string(const char * p,uint32_t max_length=UINT32_MAX)123061da546Spatrick static std::string decode_hex_ascii_string(const char *p,
124061da546Spatrick                                            uint32_t max_length = UINT32_MAX) {
125061da546Spatrick   std::string arg;
126061da546Spatrick   if (p) {
127061da546Spatrick     for (const char *c = p; ((c - p) / 2) < max_length; c += 2) {
128061da546Spatrick       int ch = decoded_hex_ascii_char(c);
129061da546Spatrick       if (ch == -1)
130061da546Spatrick         break;
131061da546Spatrick       else
132061da546Spatrick         arg.push_back(ch);
133061da546Spatrick     }
134061da546Spatrick   }
135061da546Spatrick   return arg;
136061da546Spatrick }
137061da546Spatrick 
decode_uint64(const char * p,int base,char ** end=nullptr,uint64_t fail_value=0)138061da546Spatrick uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
139061da546Spatrick                        uint64_t fail_value = 0) {
140061da546Spatrick   nub_addr_t addr = strtoull(p, end, 16);
141061da546Spatrick   if (addr == 0 && errno != 0)
142061da546Spatrick     return fail_value;
143061da546Spatrick   return addr;
144061da546Spatrick }
145061da546Spatrick 
146061da546Spatrick extern void ASLLogCallback(void *baton, uint32_t flags, const char *format,
147061da546Spatrick                            va_list args);
148061da546Spatrick 
149061da546Spatrick // from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
150061da546Spatrick extern "C" {
151061da546Spatrick #define CS_OPS_STATUS 0       /* return status */
152061da546Spatrick #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
153061da546Spatrick int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize);
154061da546Spatrick 
155061da546Spatrick // from rootless.h
156061da546Spatrick bool rootless_allows_task_for_pid(pid_t pid);
157061da546Spatrick 
158061da546Spatrick // from sys/csr.h
159061da546Spatrick typedef uint32_t csr_config_t;
160061da546Spatrick #define CSR_ALLOW_TASK_FOR_PID (1 << 2)
161061da546Spatrick int csr_check(csr_config_t mask);
162061da546Spatrick }
163061da546Spatrick 
RNBRemote()164061da546Spatrick RNBRemote::RNBRemote()
165061da546Spatrick     : m_ctx(), m_comm(), m_arch(), m_continue_thread(-1), m_thread(-1),
166061da546Spatrick       m_mutex(), m_dispatch_queue_offsets(),
167061da546Spatrick       m_dispatch_queue_offsets_addr(INVALID_NUB_ADDRESS),
168061da546Spatrick       m_qSymbol_index(UINT32_MAX), m_packets_recvd(0), m_packets(),
169061da546Spatrick       m_rx_packets(), m_rx_partial_data(), m_rx_pthread(0),
170061da546Spatrick       m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4),
171061da546Spatrick       m_extended_mode(false), m_noack_mode(false),
172061da546Spatrick       m_thread_suffix_supported(false), m_list_threads_in_stop_reply(false),
173061da546Spatrick       m_compression_minsize(384), m_enable_compression_next_send_packet(false),
174061da546Spatrick       m_compression_mode(compression_types::none) {
175061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
176061da546Spatrick   CreatePacketTable();
177061da546Spatrick }
178061da546Spatrick 
~RNBRemote()179061da546Spatrick RNBRemote::~RNBRemote() {
180061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
181061da546Spatrick   StopReadRemoteDataThread();
182061da546Spatrick }
183061da546Spatrick 
CreatePacketTable()184061da546Spatrick void RNBRemote::CreatePacketTable() {
185061da546Spatrick   // Step required to add new packets:
186061da546Spatrick   // 1 - Add new enumeration to RNBRemote::PacketEnum
187061da546Spatrick   // 2 - Create the RNBRemote::HandlePacket_ function if a new function is
188061da546Spatrick   // needed
189061da546Spatrick   // 3 - Register the Packet definition with any needed callbacks in this
190061da546Spatrick   // function
191061da546Spatrick   //          - If no response is needed for a command, then use NULL for the
192061da546Spatrick   //          normal callback
193061da546Spatrick   //          - If the packet is not supported while the target is running, use
194061da546Spatrick   //          NULL for the async callback
195061da546Spatrick   // 4 - If the packet is a standard packet (starts with a '$' character
196061da546Spatrick   //      followed by the payload and then '#' and checksum, then you are done
197061da546Spatrick   //      else go on to step 5
198061da546Spatrick   // 5 - if the packet is a fixed length packet:
199061da546Spatrick   //      - modify the switch statement for the first character in the payload
200061da546Spatrick   //        in RNBRemote::CommDataReceived so it doesn't reject the new packet
201061da546Spatrick   //        type as invalid
202061da546Spatrick   //      - modify the switch statement for the first character in the payload
203061da546Spatrick   //        in RNBRemote::GetPacketPayload and make sure the payload of the
204061da546Spatrick   //        packet
205061da546Spatrick   //        is returned correctly
206061da546Spatrick 
207061da546Spatrick   std::vector<Packet> &t = m_packets;
208061da546Spatrick   t.push_back(Packet(ack, NULL, NULL, "+", "ACK"));
209061da546Spatrick   t.push_back(Packet(nack, NULL, NULL, "-", "!ACK"));
210061da546Spatrick   t.push_back(Packet(read_memory, &RNBRemote::HandlePacket_m, NULL, "m",
211061da546Spatrick                      "Read memory"));
212061da546Spatrick   t.push_back(Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p",
213061da546Spatrick                      "Read one register"));
214061da546Spatrick   t.push_back(Packet(read_general_regs, &RNBRemote::HandlePacket_g, NULL, "g",
215061da546Spatrick                      "Read registers"));
216061da546Spatrick   t.push_back(Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M",
217061da546Spatrick                      "Write memory"));
218061da546Spatrick   t.push_back(Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P",
219061da546Spatrick                      "Write one register"));
220061da546Spatrick   t.push_back(Packet(write_general_regs, &RNBRemote::HandlePacket_G, NULL, "G",
221061da546Spatrick                      "Write registers"));
222061da546Spatrick   t.push_back(Packet(insert_mem_bp, &RNBRemote::HandlePacket_z, NULL, "Z0",
223061da546Spatrick                      "Insert memory breakpoint"));
224061da546Spatrick   t.push_back(Packet(remove_mem_bp, &RNBRemote::HandlePacket_z, NULL, "z0",
225061da546Spatrick                      "Remove memory breakpoint"));
226061da546Spatrick   t.push_back(Packet(single_step, &RNBRemote::HandlePacket_s, NULL, "s",
227061da546Spatrick                      "Single step"));
228061da546Spatrick   t.push_back(Packet(cont, &RNBRemote::HandlePacket_c, NULL, "c", "continue"));
229061da546Spatrick   t.push_back(Packet(single_step_with_sig, &RNBRemote::HandlePacket_S, NULL,
230061da546Spatrick                      "S", "Single step with signal"));
231061da546Spatrick   t.push_back(
232061da546Spatrick       Packet(set_thread, &RNBRemote::HandlePacket_H, NULL, "H", "Set thread"));
233061da546Spatrick   t.push_back(Packet(halt, &RNBRemote::HandlePacket_last_signal,
234061da546Spatrick                      &RNBRemote::HandlePacket_stop_process, "\x03", "^C"));
235061da546Spatrick   //  t.push_back (Packet (use_extended_mode,
236061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "!", "Use extended mode"));
237061da546Spatrick   t.push_back(Packet(why_halted, &RNBRemote::HandlePacket_last_signal, NULL,
238061da546Spatrick                      "?", "Why did target halt"));
239061da546Spatrick   t.push_back(
240061da546Spatrick       Packet(set_argv, &RNBRemote::HandlePacket_A, NULL, "A", "Set argv"));
241061da546Spatrick   //  t.push_back (Packet (set_bp,
242061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "B", "Set/clear
243061da546Spatrick   //  breakpoint"));
244061da546Spatrick   t.push_back(Packet(continue_with_sig, &RNBRemote::HandlePacket_C, NULL, "C",
245061da546Spatrick                      "Continue with signal"));
246061da546Spatrick   t.push_back(Packet(detach, &RNBRemote::HandlePacket_D, NULL, "D",
247061da546Spatrick                      "Detach gdb from remote system"));
248061da546Spatrick   //  t.push_back (Packet (step_inferior_one_cycle,
249061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "i", "Step inferior by one
250061da546Spatrick   //  clock cycle"));
251061da546Spatrick   //  t.push_back (Packet (signal_and_step_inf_one_cycle,
252061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "I", "Signal inferior, then
253061da546Spatrick   //  step one clock cycle"));
254061da546Spatrick   t.push_back(Packet(kill, &RNBRemote::HandlePacket_k, NULL, "k", "Kill"));
255061da546Spatrick   //  t.push_back (Packet (restart,
256061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "R", "Restart inferior"));
257061da546Spatrick   //  t.push_back (Packet (search_mem_backwards,
258061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "t", "Search memory
259061da546Spatrick   //  backwards"));
260061da546Spatrick   t.push_back(Packet(thread_alive_p, &RNBRemote::HandlePacket_T, NULL, "T",
261061da546Spatrick                      "Is thread alive"));
262061da546Spatrick   t.push_back(Packet(query_supported_features,
263061da546Spatrick                      &RNBRemote::HandlePacket_qSupported, NULL, "qSupported",
264061da546Spatrick                      "Query about supported features"));
265061da546Spatrick   t.push_back(Packet(vattach, &RNBRemote::HandlePacket_v, NULL, "vAttach",
266061da546Spatrick                      "Attach to a new process"));
267061da546Spatrick   t.push_back(Packet(vattachwait, &RNBRemote::HandlePacket_v, NULL,
268061da546Spatrick                      "vAttachWait",
269061da546Spatrick                      "Wait for a process to start up then attach to it"));
270061da546Spatrick   t.push_back(Packet(vattachorwait, &RNBRemote::HandlePacket_v, NULL,
271061da546Spatrick                      "vAttachOrWait", "Attach to the process or if it doesn't "
272061da546Spatrick                                       "exist, wait for the process to start up "
273061da546Spatrick                                       "then attach to it"));
274061da546Spatrick   t.push_back(Packet(vattachname, &RNBRemote::HandlePacket_v, NULL,
275061da546Spatrick                      "vAttachName", "Attach to an existing process by name"));
276061da546Spatrick   t.push_back(Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL,
277061da546Spatrick                      "vCont;", "Verbose resume with thread actions"));
278061da546Spatrick   t.push_back(Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL,
279061da546Spatrick                      "vCont?",
280061da546Spatrick                      "List valid continue-with-thread-actions actions"));
281061da546Spatrick   t.push_back(Packet(read_data_from_memory, &RNBRemote::HandlePacket_x, NULL,
282061da546Spatrick                      "x", "Read data from memory"));
283061da546Spatrick   t.push_back(Packet(write_data_to_memory, &RNBRemote::HandlePacket_X, NULL,
284061da546Spatrick                      "X", "Write data to memory"));
285dda28197Spatrick   t.push_back(Packet(insert_hardware_bp, &RNBRemote::HandlePacket_z, NULL, "Z1",
286dda28197Spatrick                      "Insert hardware breakpoint"));
287dda28197Spatrick   t.push_back(Packet(remove_hardware_bp, &RNBRemote::HandlePacket_z, NULL, "z1",
288dda28197Spatrick                      "Remove hardware breakpoint"));
289061da546Spatrick   t.push_back(Packet(insert_write_watch_bp, &RNBRemote::HandlePacket_z, NULL,
290061da546Spatrick                      "Z2", "Insert write watchpoint"));
291061da546Spatrick   t.push_back(Packet(remove_write_watch_bp, &RNBRemote::HandlePacket_z, NULL,
292061da546Spatrick                      "z2", "Remove write watchpoint"));
293061da546Spatrick   t.push_back(Packet(insert_read_watch_bp, &RNBRemote::HandlePacket_z, NULL,
294061da546Spatrick                      "Z3", "Insert read watchpoint"));
295061da546Spatrick   t.push_back(Packet(remove_read_watch_bp, &RNBRemote::HandlePacket_z, NULL,
296061da546Spatrick                      "z3", "Remove read watchpoint"));
297061da546Spatrick   t.push_back(Packet(insert_access_watch_bp, &RNBRemote::HandlePacket_z, NULL,
298061da546Spatrick                      "Z4", "Insert access watchpoint"));
299061da546Spatrick   t.push_back(Packet(remove_access_watch_bp, &RNBRemote::HandlePacket_z, NULL,
300061da546Spatrick                      "z4", "Remove access watchpoint"));
301061da546Spatrick   t.push_back(Packet(query_monitor, &RNBRemote::HandlePacket_qRcmd, NULL,
302061da546Spatrick                      "qRcmd", "Monitor command"));
303061da546Spatrick   t.push_back(Packet(query_current_thread_id, &RNBRemote::HandlePacket_qC, NULL,
304061da546Spatrick                      "qC", "Query current thread ID"));
305061da546Spatrick   t.push_back(Packet(query_echo, &RNBRemote::HandlePacket_qEcho, NULL, "qEcho:",
306061da546Spatrick                      "Echo the packet back to allow the debugger to sync up "
307061da546Spatrick                      "with this server"));
308061da546Spatrick   t.push_back(Packet(query_get_pid, &RNBRemote::HandlePacket_qGetPid, NULL,
309061da546Spatrick                      "qGetPid", "Query process id"));
310061da546Spatrick   t.push_back(Packet(query_thread_ids_first,
311061da546Spatrick                      &RNBRemote::HandlePacket_qThreadInfo, NULL, "qfThreadInfo",
312061da546Spatrick                      "Get list of active threads (first req)"));
313061da546Spatrick   t.push_back(Packet(query_thread_ids_subsequent,
314061da546Spatrick                      &RNBRemote::HandlePacket_qThreadInfo, NULL, "qsThreadInfo",
315061da546Spatrick                      "Get list of active threads (subsequent req)"));
316061da546Spatrick   // APPLE LOCAL: qThreadStopInfo
317061da546Spatrick   // syntax: qThreadStopInfoTTTT
318061da546Spatrick   //  TTTT is hex thread ID
319061da546Spatrick   t.push_back(Packet(query_thread_stop_info,
320061da546Spatrick                      &RNBRemote::HandlePacket_qThreadStopInfo, NULL,
321061da546Spatrick                      "qThreadStopInfo",
322061da546Spatrick                      "Get detailed info on why the specified thread stopped"));
323061da546Spatrick   t.push_back(Packet(query_thread_extra_info,
324061da546Spatrick                      &RNBRemote::HandlePacket_qThreadExtraInfo, NULL,
325061da546Spatrick                      "qThreadExtraInfo", "Get printable status of a thread"));
326061da546Spatrick   //  t.push_back (Packet (query_image_offsets,
327061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qOffsets", "Report offset
328061da546Spatrick   //  of loaded program"));
329061da546Spatrick   t.push_back(Packet(
330061da546Spatrick       query_launch_success, &RNBRemote::HandlePacket_qLaunchSuccess, NULL,
331061da546Spatrick       "qLaunchSuccess", "Report the success or failure of the launch attempt"));
332061da546Spatrick   t.push_back(
333061da546Spatrick       Packet(query_register_info, &RNBRemote::HandlePacket_qRegisterInfo, NULL,
334061da546Spatrick              "qRegisterInfo",
335061da546Spatrick              "Dynamically discover remote register context information."));
336061da546Spatrick   t.push_back(Packet(
337061da546Spatrick       query_shlib_notify_info_addr, &RNBRemote::HandlePacket_qShlibInfoAddr,
338061da546Spatrick       NULL, "qShlibInfoAddr", "Returns the address that contains info needed "
339061da546Spatrick                               "for getting shared library notifications"));
340061da546Spatrick   t.push_back(Packet(query_step_packet_supported,
341061da546Spatrick                      &RNBRemote::HandlePacket_qStepPacketSupported, NULL,
342061da546Spatrick                      "qStepPacketSupported",
343061da546Spatrick                      "Replys with OK if the 's' packet is supported."));
344061da546Spatrick   t.push_back(
345061da546Spatrick       Packet(query_vattachorwait_supported,
346061da546Spatrick              &RNBRemote::HandlePacket_qVAttachOrWaitSupported, NULL,
347061da546Spatrick              "qVAttachOrWaitSupported",
348061da546Spatrick              "Replys with OK if the 'vAttachOrWait' packet is supported."));
349061da546Spatrick   t.push_back(
350061da546Spatrick       Packet(query_sync_thread_state_supported,
351061da546Spatrick              &RNBRemote::HandlePacket_qSyncThreadStateSupported, NULL,
352061da546Spatrick              "qSyncThreadStateSupported",
353061da546Spatrick              "Replys with OK if the 'QSyncThreadState:' packet is supported."));
354061da546Spatrick   t.push_back(Packet(
355061da546Spatrick       query_host_info, &RNBRemote::HandlePacket_qHostInfo, NULL, "qHostInfo",
356061da546Spatrick       "Replies with multiple 'key:value;' tuples appended to each other."));
357061da546Spatrick   t.push_back(Packet(
358061da546Spatrick       query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion,
359061da546Spatrick       NULL, "qGDBServerVersion",
360061da546Spatrick       "Replies with multiple 'key:value;' tuples appended to each other."));
361061da546Spatrick   t.push_back(Packet(
362061da546Spatrick       query_process_info, &RNBRemote::HandlePacket_qProcessInfo, NULL,
363061da546Spatrick       "qProcessInfo",
364061da546Spatrick       "Replies with multiple 'key:value;' tuples appended to each other."));
365061da546Spatrick   t.push_back(Packet(
366061da546Spatrick       query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol, NULL, "qSymbol:",
367061da546Spatrick       "Notify that host debugger is ready to do symbol lookups"));
368061da546Spatrick   t.push_back(Packet(json_query_thread_extended_info,
369061da546Spatrick                      &RNBRemote::HandlePacket_jThreadExtendedInfo, NULL,
370061da546Spatrick                      "jThreadExtendedInfo",
371061da546Spatrick                      "Replies with JSON data of thread extended information."));
372061da546Spatrick   t.push_back(Packet(json_query_get_loaded_dynamic_libraries_infos,
373061da546Spatrick                      &RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos,
374061da546Spatrick                      NULL, "jGetLoadedDynamicLibrariesInfos",
375061da546Spatrick                      "Replies with JSON data of all the shared libraries "
376061da546Spatrick                      "loaded in this process."));
377061da546Spatrick   t.push_back(
378061da546Spatrick       Packet(json_query_threads_info, &RNBRemote::HandlePacket_jThreadsInfo,
379061da546Spatrick              NULL, "jThreadsInfo",
380061da546Spatrick              "Replies with JSON data with information about all threads."));
381061da546Spatrick   t.push_back(Packet(json_query_get_shared_cache_info,
382061da546Spatrick                      &RNBRemote::HandlePacket_jGetSharedCacheInfo, NULL,
383061da546Spatrick                      "jGetSharedCacheInfo", "Replies with JSON data about the "
384061da546Spatrick                                             "location and uuid of the shared "
385061da546Spatrick                                             "cache in the inferior process."));
386061da546Spatrick   t.push_back(Packet(start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode,
387061da546Spatrick                      NULL, "QStartNoAckMode",
388061da546Spatrick                      "Request that " DEBUGSERVER_PROGRAM_NAME
389061da546Spatrick                      " stop acking remote protocol packets"));
390061da546Spatrick   t.push_back(Packet(prefix_reg_packets_with_tid,
391061da546Spatrick                      &RNBRemote::HandlePacket_QThreadSuffixSupported, NULL,
392061da546Spatrick                      "QThreadSuffixSupported",
393061da546Spatrick                      "Check if thread specific packets (register packets 'g', "
394061da546Spatrick                      "'G', 'p', and 'P') support having the thread ID appended "
395061da546Spatrick                      "to the end of the command"));
396061da546Spatrick   t.push_back(Packet(set_logging_mode, &RNBRemote::HandlePacket_QSetLogging,
397*f6aab3d8Srobert                      NULL, "QSetLogging:", "Turn on log channels in debugserver"));
398*f6aab3d8Srobert   t.push_back(Packet(set_ignored_exceptions, &RNBRemote::HandlePacket_QSetIgnoredExceptions,
399*f6aab3d8Srobert                      NULL, "QSetIgnoredExceptions:", "Set the exception types "
400*f6aab3d8Srobert                                            "debugserver won't wait for, allowing "
401*f6aab3d8Srobert                                            "them to be turned into the equivalent "
402*f6aab3d8Srobert                                            "BSD signals by the normal means."));
403061da546Spatrick   t.push_back(Packet(
404061da546Spatrick       set_max_packet_size, &RNBRemote::HandlePacket_QSetMaxPacketSize, NULL,
405061da546Spatrick       "QSetMaxPacketSize:",
406061da546Spatrick       "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle"));
407061da546Spatrick   t.push_back(Packet(
408061da546Spatrick       set_max_payload_size, &RNBRemote::HandlePacket_QSetMaxPayloadSize, NULL,
409061da546Spatrick       "QSetMaxPayloadSize:", "Tell " DEBUGSERVER_PROGRAM_NAME
410061da546Spatrick                              " the max sized payload gdb can handle"));
411061da546Spatrick   t.push_back(
412061da546Spatrick       Packet(set_environment_variable, &RNBRemote::HandlePacket_QEnvironment,
413061da546Spatrick              NULL, "QEnvironment:",
414061da546Spatrick              "Add an environment variable to the inferior's environment"));
415061da546Spatrick   t.push_back(
416061da546Spatrick       Packet(set_environment_variable_hex,
417061da546Spatrick              &RNBRemote::HandlePacket_QEnvironmentHexEncoded, NULL,
418061da546Spatrick              "QEnvironmentHexEncoded:",
419061da546Spatrick              "Add an environment variable to the inferior's environment"));
420061da546Spatrick   t.push_back(Packet(set_launch_arch, &RNBRemote::HandlePacket_QLaunchArch,
421061da546Spatrick                      NULL, "QLaunchArch:", "Set the architecture to use when "
422061da546Spatrick                                            "launching a process for hosts that "
423061da546Spatrick                                            "can run multiple architecture "
424061da546Spatrick                                            "slices from universal files."));
425061da546Spatrick   t.push_back(Packet(set_disable_aslr, &RNBRemote::HandlePacket_QSetDisableASLR,
426061da546Spatrick                      NULL, "QSetDisableASLR:",
427061da546Spatrick                      "Set whether to disable ASLR when launching the process "
428061da546Spatrick                      "with the set argv ('A') packet"));
429061da546Spatrick   t.push_back(Packet(set_stdin, &RNBRemote::HandlePacket_QSetSTDIO, NULL,
430061da546Spatrick                      "QSetSTDIN:", "Set the standard input for a process to be "
431061da546Spatrick                                    "launched with the 'A' packet"));
432061da546Spatrick   t.push_back(Packet(set_stdout, &RNBRemote::HandlePacket_QSetSTDIO, NULL,
433061da546Spatrick                      "QSetSTDOUT:", "Set the standard output for a process to "
434061da546Spatrick                                     "be launched with the 'A' packet"));
435061da546Spatrick   t.push_back(Packet(set_stderr, &RNBRemote::HandlePacket_QSetSTDIO, NULL,
436061da546Spatrick                      "QSetSTDERR:", "Set the standard error for a process to "
437061da546Spatrick                                     "be launched with the 'A' packet"));
438061da546Spatrick   t.push_back(Packet(set_working_dir, &RNBRemote::HandlePacket_QSetWorkingDir,
439061da546Spatrick                      NULL, "QSetWorkingDir:", "Set the working directory for a "
440061da546Spatrick                                               "process to be launched with the "
441061da546Spatrick                                               "'A' packet"));
442061da546Spatrick   t.push_back(Packet(set_list_threads_in_stop_reply,
443061da546Spatrick                      &RNBRemote::HandlePacket_QListThreadsInStopReply, NULL,
444061da546Spatrick                      "QListThreadsInStopReply",
445061da546Spatrick                      "Set if the 'threads' key should be added to the stop "
446061da546Spatrick                      "reply packets with a list of all thread IDs."));
447061da546Spatrick   t.push_back(Packet(
448061da546Spatrick       sync_thread_state, &RNBRemote::HandlePacket_QSyncThreadState, NULL,
449061da546Spatrick       "QSyncThreadState:", "Do whatever is necessary to make sure 'thread' is "
450061da546Spatrick                            "in a safe state to call functions on."));
451061da546Spatrick   //  t.push_back (Packet (pass_signals_to_inferior,
452061da546Spatrick   //  &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "QPassSignals:", "Specify
453061da546Spatrick   //  which signals are passed to the inferior"));
454061da546Spatrick   t.push_back(Packet(allocate_memory, &RNBRemote::HandlePacket_AllocateMemory,
455061da546Spatrick                      NULL, "_M", "Allocate memory in the inferior process."));
456061da546Spatrick   t.push_back(Packet(deallocate_memory,
457061da546Spatrick                      &RNBRemote::HandlePacket_DeallocateMemory, NULL, "_m",
458061da546Spatrick                      "Deallocate memory in the inferior process."));
459061da546Spatrick   t.push_back(Packet(
460061da546Spatrick       save_register_state, &RNBRemote::HandlePacket_SaveRegisterState, NULL,
461061da546Spatrick       "QSaveRegisterState", "Save the register state for the current thread "
462061da546Spatrick                             "and return a decimal save ID."));
463061da546Spatrick   t.push_back(Packet(restore_register_state,
464061da546Spatrick                      &RNBRemote::HandlePacket_RestoreRegisterState, NULL,
465061da546Spatrick                      "QRestoreRegisterState:",
466061da546Spatrick                      "Restore the register state given a save ID previously "
467061da546Spatrick                      "returned from a call to QSaveRegisterState."));
468061da546Spatrick   t.push_back(Packet(
469061da546Spatrick       memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL,
470061da546Spatrick       "qMemoryRegionInfo", "Return size and attributes of a memory region that "
471061da546Spatrick                            "contains the given address"));
472061da546Spatrick   t.push_back(Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData,
473061da546Spatrick                      NULL, "qGetProfileData",
474061da546Spatrick                      "Return profiling data of the current target."));
475061da546Spatrick   t.push_back(Packet(set_enable_profiling,
476061da546Spatrick                      &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL,
477061da546Spatrick                      "QSetEnableAsyncProfiling",
478061da546Spatrick                      "Enable or disable the profiling of current target."));
479061da546Spatrick   t.push_back(Packet(enable_compression,
480061da546Spatrick                      &RNBRemote::HandlePacket_QEnableCompression, NULL,
481061da546Spatrick                      "QEnableCompression:",
482061da546Spatrick                      "Enable compression for the remainder of the connection"));
483061da546Spatrick   t.push_back(Packet(watchpoint_support_info,
484061da546Spatrick                      &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL,
485061da546Spatrick                      "qWatchpointSupportInfo",
486061da546Spatrick                      "Return the number of supported hardware watchpoints"));
487061da546Spatrick   t.push_back(Packet(set_process_event,
488061da546Spatrick                      &RNBRemote::HandlePacket_QSetProcessEvent, NULL,
489061da546Spatrick                      "QSetProcessEvent:", "Set a process event, to be passed "
490061da546Spatrick                                           "to the process, can be set before "
491061da546Spatrick                                           "the process is started, or after."));
492061da546Spatrick   t.push_back(
493061da546Spatrick       Packet(set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError,
494061da546Spatrick              NULL, "QSetDetachOnError:",
495061da546Spatrick              "Set whether debugserver will detach (1) or kill (0) from the "
496061da546Spatrick              "process it is controlling if it loses connection to lldb."));
497061da546Spatrick   t.push_back(Packet(
498061da546Spatrick       speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:",
499061da546Spatrick       "Test the maximum speed at which packet can be sent/received."));
500061da546Spatrick   t.push_back(Packet(query_transfer, &RNBRemote::HandlePacket_qXfer, NULL,
501061da546Spatrick                      "qXfer:", "Support the qXfer packet."));
502*f6aab3d8Srobert   t.push_back(Packet(json_query_dyld_process_state,
503*f6aab3d8Srobert                      &RNBRemote::HandlePacket_jGetDyldProcessState, NULL,
504*f6aab3d8Srobert                      "jGetDyldProcessState",
505*f6aab3d8Srobert                      "Query the process state from dyld."));
506061da546Spatrick }
507061da546Spatrick 
FlushSTDIO()508061da546Spatrick void RNBRemote::FlushSTDIO() {
509061da546Spatrick   if (m_ctx.HasValidProcessID()) {
510061da546Spatrick     nub_process_t pid = m_ctx.ProcessID();
511061da546Spatrick     char buf[256];
512061da546Spatrick     nub_size_t count;
513061da546Spatrick     do {
514061da546Spatrick       count = DNBProcessGetAvailableSTDOUT(pid, buf, sizeof(buf));
515061da546Spatrick       if (count > 0) {
516061da546Spatrick         SendSTDOUTPacket(buf, count);
517061da546Spatrick       }
518061da546Spatrick     } while (count > 0);
519061da546Spatrick 
520061da546Spatrick     do {
521061da546Spatrick       count = DNBProcessGetAvailableSTDERR(pid, buf, sizeof(buf));
522061da546Spatrick       if (count > 0) {
523061da546Spatrick         SendSTDERRPacket(buf, count);
524061da546Spatrick       }
525061da546Spatrick     } while (count > 0);
526061da546Spatrick   }
527061da546Spatrick }
528061da546Spatrick 
SendAsyncProfileData()529061da546Spatrick void RNBRemote::SendAsyncProfileData() {
530061da546Spatrick   if (m_ctx.HasValidProcessID()) {
531061da546Spatrick     nub_process_t pid = m_ctx.ProcessID();
532061da546Spatrick     char buf[1024];
533061da546Spatrick     nub_size_t count;
534061da546Spatrick     do {
535061da546Spatrick       count = DNBProcessGetAvailableProfileData(pid, buf, sizeof(buf));
536061da546Spatrick       if (count > 0) {
537061da546Spatrick         SendAsyncProfileDataPacket(buf, count);
538061da546Spatrick       }
539061da546Spatrick     } while (count > 0);
540061da546Spatrick   }
541061da546Spatrick }
542061da546Spatrick 
SendHexEncodedBytePacket(const char * header,const void * buf,size_t buf_len,const char * footer)543061da546Spatrick rnb_err_t RNBRemote::SendHexEncodedBytePacket(const char *header,
544061da546Spatrick                                               const void *buf, size_t buf_len,
545061da546Spatrick                                               const char *footer) {
546061da546Spatrick   std::ostringstream packet_sstrm;
547061da546Spatrick   // Append the header cstr if there was one
548061da546Spatrick   if (header && header[0])
549061da546Spatrick     packet_sstrm << header;
550061da546Spatrick   nub_size_t i;
551061da546Spatrick   const uint8_t *ubuf8 = (const uint8_t *)buf;
552061da546Spatrick   for (i = 0; i < buf_len; i++) {
553061da546Spatrick     packet_sstrm << RAWHEX8(ubuf8[i]);
554061da546Spatrick   }
555061da546Spatrick   // Append the footer cstr if there was one
556061da546Spatrick   if (footer && footer[0])
557061da546Spatrick     packet_sstrm << footer;
558061da546Spatrick 
559061da546Spatrick   return SendPacket(packet_sstrm.str());
560061da546Spatrick }
561061da546Spatrick 
SendSTDOUTPacket(char * buf,nub_size_t buf_size)562061da546Spatrick rnb_err_t RNBRemote::SendSTDOUTPacket(char *buf, nub_size_t buf_size) {
563061da546Spatrick   if (buf_size == 0)
564061da546Spatrick     return rnb_success;
565061da546Spatrick   return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
566061da546Spatrick }
567061da546Spatrick 
SendSTDERRPacket(char * buf,nub_size_t buf_size)568061da546Spatrick rnb_err_t RNBRemote::SendSTDERRPacket(char *buf, nub_size_t buf_size) {
569061da546Spatrick   if (buf_size == 0)
570061da546Spatrick     return rnb_success;
571061da546Spatrick   return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
572061da546Spatrick }
573061da546Spatrick 
574061da546Spatrick // This makes use of asynchronous bit 'A' in the gdb remote protocol.
SendAsyncProfileDataPacket(char * buf,nub_size_t buf_size)575061da546Spatrick rnb_err_t RNBRemote::SendAsyncProfileDataPacket(char *buf,
576061da546Spatrick                                                 nub_size_t buf_size) {
577061da546Spatrick   if (buf_size == 0)
578061da546Spatrick     return rnb_success;
579061da546Spatrick 
580061da546Spatrick   std::string packet("A");
581061da546Spatrick   packet.append(buf, buf_size);
582061da546Spatrick   return SendPacket(packet);
583061da546Spatrick }
584061da546Spatrick 
585061da546Spatrick rnb_err_t
SendAsyncJSONPacket(const JSONGenerator::Dictionary & dictionary)586061da546Spatrick RNBRemote::SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary) {
587061da546Spatrick   std::ostringstream stream;
588061da546Spatrick   // We're choosing something that is easy to spot if we somehow get one
589061da546Spatrick   // of these coming out at the wrong time (i.e. when the remote side
590061da546Spatrick   // is not waiting for a process control completion response).
591061da546Spatrick   stream << "JSON-async:";
592*f6aab3d8Srobert   dictionary.DumpBinaryEscaped(stream);
593*f6aab3d8Srobert   return SendPacket(stream.str());
594061da546Spatrick }
595061da546Spatrick 
596061da546Spatrick // Given a std::string packet contents to send, possibly encode/compress it.
597061da546Spatrick // If compression is enabled, the returned std::string will be in one of two
598061da546Spatrick // forms:
599061da546Spatrick //
600061da546Spatrick //    N<original packet contents uncompressed>
601061da546Spatrick //    C<size of original decompressed packet>:<packet compressed with the
602061da546Spatrick //    requested compression scheme>
603061da546Spatrick //
604061da546Spatrick // If compression is not requested, the original packet contents are returned
605061da546Spatrick 
CompressString(const std::string & orig)606061da546Spatrick std::string RNBRemote::CompressString(const std::string &orig) {
607061da546Spatrick   std::string compressed;
608061da546Spatrick   compression_types compression_type = GetCompressionType();
609061da546Spatrick   if (compression_type != compression_types::none) {
610061da546Spatrick     bool compress_this_packet = false;
611061da546Spatrick 
612061da546Spatrick     if (orig.size() > m_compression_minsize) {
613061da546Spatrick       compress_this_packet = true;
614061da546Spatrick     }
615061da546Spatrick 
616061da546Spatrick     if (compress_this_packet) {
617061da546Spatrick       const size_t encoded_data_buf_size = orig.size() + 128;
618061da546Spatrick       std::vector<uint8_t> encoded_data(encoded_data_buf_size);
619061da546Spatrick       size_t compressed_size = 0;
620061da546Spatrick 
621061da546Spatrick       // Allocate a scratch buffer for libcompression the first
622061da546Spatrick       // time we see a different compression type; reuse it in
623061da546Spatrick       // all compression_encode_buffer calls so it doesn't need
624061da546Spatrick       // to allocate / free its own scratch buffer each time.
625061da546Spatrick       // This buffer will only be freed when compression type
626061da546Spatrick       // changes; otherwise it will persist until debugserver
627061da546Spatrick       // exit.
628061da546Spatrick 
629061da546Spatrick       static compression_types g_libcompress_scratchbuf_type = compression_types::none;
630061da546Spatrick       static void *g_libcompress_scratchbuf = nullptr;
631061da546Spatrick 
632061da546Spatrick       if (g_libcompress_scratchbuf_type != compression_type) {
633061da546Spatrick         if (g_libcompress_scratchbuf) {
634061da546Spatrick           free (g_libcompress_scratchbuf);
635061da546Spatrick           g_libcompress_scratchbuf = nullptr;
636061da546Spatrick         }
637061da546Spatrick         size_t scratchbuf_size = 0;
638061da546Spatrick         switch (compression_type) {
639061da546Spatrick           case compression_types::lz4:
640061da546Spatrick             scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZ4_RAW);
641061da546Spatrick             break;
642061da546Spatrick           case compression_types::zlib_deflate:
643061da546Spatrick             scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_ZLIB);
644061da546Spatrick             break;
645061da546Spatrick           case compression_types::lzma:
646061da546Spatrick             scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZMA);
647061da546Spatrick             break;
648061da546Spatrick           case compression_types::lzfse:
649061da546Spatrick             scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZFSE);
650061da546Spatrick             break;
651061da546Spatrick           default:
652061da546Spatrick             break;
653061da546Spatrick         }
654061da546Spatrick         if (scratchbuf_size > 0) {
655061da546Spatrick           g_libcompress_scratchbuf = (void*) malloc (scratchbuf_size);
656061da546Spatrick           g_libcompress_scratchbuf_type = compression_type;
657061da546Spatrick         }
658061da546Spatrick       }
659061da546Spatrick 
660061da546Spatrick       if (compression_type == compression_types::lz4) {
661061da546Spatrick         compressed_size = compression_encode_buffer(
662061da546Spatrick             encoded_data.data(), encoded_data_buf_size,
663061da546Spatrick             (const uint8_t *)orig.c_str(), orig.size(),
664061da546Spatrick             g_libcompress_scratchbuf,
665061da546Spatrick             COMPRESSION_LZ4_RAW);
666061da546Spatrick       }
667061da546Spatrick       if (compression_type == compression_types::zlib_deflate) {
668061da546Spatrick         compressed_size = compression_encode_buffer(
669061da546Spatrick             encoded_data.data(), encoded_data_buf_size,
670061da546Spatrick             (const uint8_t *)orig.c_str(), orig.size(),
671061da546Spatrick             g_libcompress_scratchbuf,
672061da546Spatrick             COMPRESSION_ZLIB);
673061da546Spatrick       }
674061da546Spatrick       if (compression_type == compression_types::lzma) {
675061da546Spatrick         compressed_size = compression_encode_buffer(
676061da546Spatrick             encoded_data.data(), encoded_data_buf_size,
677061da546Spatrick             (const uint8_t *)orig.c_str(), orig.size(),
678061da546Spatrick             g_libcompress_scratchbuf,
679061da546Spatrick             COMPRESSION_LZMA);
680061da546Spatrick       }
681061da546Spatrick       if (compression_type == compression_types::lzfse) {
682061da546Spatrick         compressed_size = compression_encode_buffer(
683061da546Spatrick             encoded_data.data(), encoded_data_buf_size,
684061da546Spatrick             (const uint8_t *)orig.c_str(), orig.size(),
685061da546Spatrick             g_libcompress_scratchbuf,
686061da546Spatrick             COMPRESSION_LZFSE);
687061da546Spatrick       }
688061da546Spatrick 
689061da546Spatrick       if (compressed_size > 0) {
690061da546Spatrick         compressed.clear();
691061da546Spatrick         compressed.reserve(compressed_size);
692061da546Spatrick         compressed = "C";
693061da546Spatrick         char numbuf[16];
694061da546Spatrick         snprintf(numbuf, sizeof(numbuf), "%zu:", orig.size());
695061da546Spatrick         numbuf[sizeof(numbuf) - 1] = '\0';
696061da546Spatrick         compressed.append(numbuf);
697061da546Spatrick 
698061da546Spatrick         for (size_t i = 0; i < compressed_size; i++) {
699061da546Spatrick           uint8_t byte = encoded_data[i];
700061da546Spatrick           if (byte == '#' || byte == '$' || byte == '}' || byte == '*' ||
701061da546Spatrick               byte == '\0') {
702061da546Spatrick             compressed.push_back(0x7d);
703061da546Spatrick             compressed.push_back(byte ^ 0x20);
704061da546Spatrick           } else {
705061da546Spatrick             compressed.push_back(byte);
706061da546Spatrick           }
707061da546Spatrick         }
708061da546Spatrick       } else {
709061da546Spatrick         compressed = "N" + orig;
710061da546Spatrick       }
711061da546Spatrick     } else {
712061da546Spatrick       compressed = "N" + orig;
713061da546Spatrick     }
714061da546Spatrick   } else {
715061da546Spatrick     compressed = orig;
716061da546Spatrick   }
717061da546Spatrick 
718061da546Spatrick   return compressed;
719061da546Spatrick }
720061da546Spatrick 
SendPacket(const std::string & s)721061da546Spatrick rnb_err_t RNBRemote::SendPacket(const std::string &s) {
722061da546Spatrick   DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called",
723061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
724061da546Spatrick                    __FUNCTION__, s.c_str());
725061da546Spatrick 
726061da546Spatrick   std::string s_compressed = CompressString(s);
727061da546Spatrick 
728061da546Spatrick   std::string sendpacket = "$" + s_compressed + "#";
729061da546Spatrick   int cksum = 0;
730061da546Spatrick   char hexbuf[5];
731061da546Spatrick 
732061da546Spatrick   if (m_noack_mode) {
733061da546Spatrick     sendpacket += "00";
734061da546Spatrick   } else {
735061da546Spatrick     for (size_t i = 0; i != s_compressed.size(); ++i)
736061da546Spatrick       cksum += s_compressed[i];
737061da546Spatrick     snprintf(hexbuf, sizeof hexbuf, "%02x", cksum & 0xff);
738061da546Spatrick     sendpacket += hexbuf;
739061da546Spatrick   }
740061da546Spatrick 
741061da546Spatrick   rnb_err_t err = m_comm.Write(sendpacket.c_str(), sendpacket.size());
742061da546Spatrick   if (err != rnb_success)
743061da546Spatrick     return err;
744061da546Spatrick 
745061da546Spatrick   if (m_noack_mode)
746061da546Spatrick     return rnb_success;
747061da546Spatrick 
748061da546Spatrick   std::string reply;
749061da546Spatrick   RNBRemote::Packet packet;
750061da546Spatrick   err = GetPacket(reply, packet, true);
751061da546Spatrick 
752061da546Spatrick   if (err != rnb_success) {
753061da546Spatrick     DNBLogThreadedIf(LOG_RNB_REMOTE,
754061da546Spatrick                      "%8d RNBRemote::%s (%s) got error trying to get reply...",
755061da546Spatrick                      (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
756061da546Spatrick                      __FUNCTION__, sendpacket.c_str());
757061da546Spatrick     return err;
758061da546Spatrick   }
759061da546Spatrick 
760061da546Spatrick   DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) got reply: '%s'",
761061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
762061da546Spatrick                    __FUNCTION__, sendpacket.c_str(), reply.c_str());
763061da546Spatrick 
764061da546Spatrick   if (packet.type == ack)
765061da546Spatrick     return rnb_success;
766061da546Spatrick 
767061da546Spatrick   // Should we try to resend the packet at this layer?
768061da546Spatrick   //  if (packet.command == nack)
769061da546Spatrick   return rnb_err;
770061da546Spatrick }
771061da546Spatrick 
772061da546Spatrick /* Get a packet via gdb remote protocol.
773061da546Spatrick  Strip off the prefix/suffix, verify the checksum to make sure
774061da546Spatrick  a valid packet was received, send an ACK if they match.  */
775061da546Spatrick 
GetPacketPayload(std::string & return_packet)776061da546Spatrick rnb_err_t RNBRemote::GetPacketPayload(std::string &return_packet) {
777061da546Spatrick   // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s called",
778061da546Spatrick   // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
779061da546Spatrick 
780061da546Spatrick   PThreadMutex::Locker locker(m_mutex);
781061da546Spatrick   if (m_rx_packets.empty()) {
782061da546Spatrick     // Only reset the remote command available event if we have no more packets
783061da546Spatrick     m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available);
784061da546Spatrick     // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s error: no packets
785061da546Spatrick     // available...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
786061da546Spatrick     // __FUNCTION__);
787061da546Spatrick     return rnb_err;
788061da546Spatrick   }
789061da546Spatrick 
790061da546Spatrick   // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s has %u queued packets",
791061da546Spatrick   // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
792061da546Spatrick   // m_rx_packets.size());
793061da546Spatrick   return_packet.swap(m_rx_packets.front());
794061da546Spatrick   m_rx_packets.pop_front();
795061da546Spatrick   locker.Reset(); // Release our lock on the mutex
796061da546Spatrick 
797061da546Spatrick   if (m_rx_packets.empty()) {
798061da546Spatrick     // Reset the remote command available event if we have no more packets
799061da546Spatrick     m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available);
800061da546Spatrick   }
801061da546Spatrick 
802061da546Spatrick   // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s: '%s'",
803061da546Spatrick   // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
804061da546Spatrick   // return_packet.c_str());
805061da546Spatrick 
806061da546Spatrick   switch (return_packet[0]) {
807061da546Spatrick   case '+':
808061da546Spatrick   case '-':
809061da546Spatrick   case '\x03':
810061da546Spatrick     break;
811061da546Spatrick 
812061da546Spatrick   case '$': {
813061da546Spatrick     long packet_checksum = 0;
814061da546Spatrick     if (!m_noack_mode) {
815061da546Spatrick       for (size_t i = return_packet.size() - 2; i < return_packet.size(); ++i) {
816061da546Spatrick         char checksum_char = tolower(return_packet[i]);
817061da546Spatrick         if (!isxdigit(checksum_char)) {
818061da546Spatrick           m_comm.Write("-", 1);
819061da546Spatrick           DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s error: packet "
820061da546Spatrick                                            "with invalid checksum characters: "
821061da546Spatrick                                            "%s",
822061da546Spatrick                            (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
823061da546Spatrick                            __FUNCTION__, return_packet.c_str());
824061da546Spatrick           return rnb_err;
825061da546Spatrick         }
826061da546Spatrick       }
827061da546Spatrick       packet_checksum =
828061da546Spatrick           strtol(&return_packet[return_packet.size() - 2], NULL, 16);
829061da546Spatrick     }
830061da546Spatrick 
831061da546Spatrick     return_packet.erase(0, 1);                     // Strip the leading '$'
832061da546Spatrick     return_packet.erase(return_packet.size() - 3); // Strip the #XX checksum
833061da546Spatrick 
834061da546Spatrick     if (!m_noack_mode) {
835061da546Spatrick       // Compute the checksum
836061da546Spatrick       int computed_checksum = 0;
837061da546Spatrick       for (std::string::iterator it = return_packet.begin();
838061da546Spatrick            it != return_packet.end(); ++it) {
839061da546Spatrick         computed_checksum += *it;
840061da546Spatrick       }
841061da546Spatrick 
842061da546Spatrick       if (packet_checksum == (computed_checksum & 0xff)) {
843061da546Spatrick         // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for
844061da546Spatrick         // '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
845061da546Spatrick         // __FUNCTION__, return_packet.c_str());
846061da546Spatrick         m_comm.Write("+", 1);
847061da546Spatrick       } else {
848061da546Spatrick         DNBLogThreadedIf(
849061da546Spatrick             LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s' (error: "
850061da546Spatrick                             "packet checksum mismatch  (0x%2.2lx != 0x%2.2x))",
851061da546Spatrick             (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
852061da546Spatrick             return_packet.c_str(), packet_checksum, computed_checksum);
853061da546Spatrick         m_comm.Write("-", 1);
854061da546Spatrick         return rnb_err;
855061da546Spatrick       }
856061da546Spatrick     }
857061da546Spatrick   } break;
858061da546Spatrick 
859061da546Spatrick   default:
860061da546Spatrick     DNBLogThreadedIf(LOG_RNB_REMOTE,
861061da546Spatrick                      "%8u RNBRemote::%s tossing unexpected packet???? %s",
862061da546Spatrick                      (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
863061da546Spatrick                      __FUNCTION__, return_packet.c_str());
864061da546Spatrick     if (!m_noack_mode)
865061da546Spatrick       m_comm.Write("-", 1);
866061da546Spatrick     return rnb_err;
867061da546Spatrick   }
868061da546Spatrick 
869061da546Spatrick   return rnb_success;
870061da546Spatrick }
871061da546Spatrick 
HandlePacket_UNIMPLEMENTED(const char * p)872061da546Spatrick rnb_err_t RNBRemote::HandlePacket_UNIMPLEMENTED(const char *p) {
873061da546Spatrick   DNBLogThreadedIf(LOG_RNB_MAX, "%8u RNBRemote::%s(\"%s\")",
874061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
875061da546Spatrick                    __FUNCTION__, p ? p : "NULL");
876061da546Spatrick   return SendPacket("");
877061da546Spatrick }
878061da546Spatrick 
HandlePacket_ILLFORMED(const char * file,int line,const char * p,const char * description)879061da546Spatrick rnb_err_t RNBRemote::HandlePacket_ILLFORMED(const char *file, int line,
880061da546Spatrick                                             const char *p,
881061da546Spatrick                                             const char *description) {
882061da546Spatrick   DNBLogThreadedIf(LOG_RNB_PACKETS, "%8u %s:%i ILLFORMED: '%s' (%s)",
883061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), file,
884061da546Spatrick                    line, __FUNCTION__, p);
885061da546Spatrick   return SendPacket("E03");
886061da546Spatrick }
887061da546Spatrick 
GetPacket(std::string & packet_payload,RNBRemote::Packet & packet_info,bool wait)888061da546Spatrick rnb_err_t RNBRemote::GetPacket(std::string &packet_payload,
889061da546Spatrick                                RNBRemote::Packet &packet_info, bool wait) {
890061da546Spatrick   std::string payload;
891061da546Spatrick   rnb_err_t err = GetPacketPayload(payload);
892061da546Spatrick   if (err != rnb_success) {
893061da546Spatrick     PThreadEvent &events = m_ctx.Events();
894061da546Spatrick     nub_event_t set_events = events.GetEventBits();
895061da546Spatrick     // TODO: add timeout version of GetPacket?? We would then need to pass
896061da546Spatrick     // that timeout value along to DNBProcessTimedWaitForEvent.
897061da546Spatrick     if (!wait || ((set_events & RNBContext::event_read_thread_running) == 0))
898061da546Spatrick       return err;
899061da546Spatrick 
900061da546Spatrick     const nub_event_t events_to_wait_for =
901061da546Spatrick         RNBContext::event_read_packet_available |
902061da546Spatrick         RNBContext::event_read_thread_exiting;
903061da546Spatrick 
904061da546Spatrick     while ((set_events = events.WaitForSetEvents(events_to_wait_for)) != 0) {
905061da546Spatrick       if (set_events & RNBContext::event_read_packet_available) {
906061da546Spatrick         // Try the queue again now that we got an event
907061da546Spatrick         err = GetPacketPayload(payload);
908061da546Spatrick         if (err == rnb_success)
909061da546Spatrick           break;
910061da546Spatrick       }
911061da546Spatrick 
912061da546Spatrick       if (set_events & RNBContext::event_read_thread_exiting)
913061da546Spatrick         err = rnb_not_connected;
914061da546Spatrick 
915061da546Spatrick       if (err == rnb_not_connected)
916061da546Spatrick         return err;
917061da546Spatrick     }
918061da546Spatrick     while (err == rnb_err)
919061da546Spatrick       ;
920061da546Spatrick 
921061da546Spatrick     if (set_events == 0)
922061da546Spatrick       err = rnb_not_connected;
923061da546Spatrick   }
924061da546Spatrick 
925061da546Spatrick   if (err == rnb_success) {
926061da546Spatrick     Packet::iterator it;
927061da546Spatrick     for (it = m_packets.begin(); it != m_packets.end(); ++it) {
928061da546Spatrick       if (payload.compare(0, it->abbrev.size(), it->abbrev) == 0)
929061da546Spatrick         break;
930061da546Spatrick     }
931061da546Spatrick 
932061da546Spatrick     // A packet we don't have an entry for. This can happen when we
933061da546Spatrick     // get a packet that we don't know about or support. We just reply
934061da546Spatrick     // accordingly and go on.
935061da546Spatrick     if (it == m_packets.end()) {
936061da546Spatrick       DNBLogThreadedIf(LOG_RNB_PACKETS, "unimplemented packet: '%s'",
937061da546Spatrick                        payload.c_str());
938061da546Spatrick       HandlePacket_UNIMPLEMENTED(payload.c_str());
939061da546Spatrick       return rnb_err;
940061da546Spatrick     } else {
941061da546Spatrick       packet_info = *it;
942061da546Spatrick       packet_payload = payload;
943061da546Spatrick     }
944061da546Spatrick   }
945061da546Spatrick   return err;
946061da546Spatrick }
947061da546Spatrick 
HandleAsyncPacket(PacketEnum * type)948061da546Spatrick rnb_err_t RNBRemote::HandleAsyncPacket(PacketEnum *type) {
949061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s",
950061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
951061da546Spatrick                    __FUNCTION__);
952061da546Spatrick   static DNBTimer g_packetTimer(true);
953061da546Spatrick   rnb_err_t err = rnb_err;
954061da546Spatrick   std::string packet_data;
955061da546Spatrick   RNBRemote::Packet packet_info;
956061da546Spatrick   err = GetPacket(packet_data, packet_info, false);
957061da546Spatrick 
958061da546Spatrick   if (err == rnb_success) {
959061da546Spatrick     if (!packet_data.empty() && isprint(packet_data[0]))
960061da546Spatrick       DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS,
961061da546Spatrick                        "HandleAsyncPacket (\"%s\");", packet_data.c_str());
962061da546Spatrick     else
963061da546Spatrick       DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS,
964061da546Spatrick                        "HandleAsyncPacket (%s);",
965061da546Spatrick                        packet_info.printable_name.c_str());
966061da546Spatrick 
967061da546Spatrick     HandlePacketCallback packet_callback = packet_info.async;
968061da546Spatrick     if (packet_callback != NULL) {
969061da546Spatrick       if (type != NULL)
970061da546Spatrick         *type = packet_info.type;
971061da546Spatrick       return (this->*packet_callback)(packet_data.c_str());
972061da546Spatrick     }
973061da546Spatrick   }
974061da546Spatrick 
975061da546Spatrick   return err;
976061da546Spatrick }
977061da546Spatrick 
HandleReceivedPacket(PacketEnum * type)978061da546Spatrick rnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) {
979061da546Spatrick   static DNBTimer g_packetTimer(true);
980061da546Spatrick 
981061da546Spatrick   //  DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s",
982061da546Spatrick   //  (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
983061da546Spatrick   rnb_err_t err = rnb_err;
984061da546Spatrick   std::string packet_data;
985061da546Spatrick   RNBRemote::Packet packet_info;
986061da546Spatrick   err = GetPacket(packet_data, packet_info, false);
987061da546Spatrick 
988061da546Spatrick   if (err == rnb_success) {
989061da546Spatrick     DNBLogThreadedIf(LOG_RNB_REMOTE, "HandleReceivedPacket (\"%s\");",
990061da546Spatrick                      packet_data.c_str());
991061da546Spatrick     HandlePacketCallback packet_callback = packet_info.normal;
992061da546Spatrick     if (packet_callback != NULL) {
993061da546Spatrick       if (type != NULL)
994061da546Spatrick         *type = packet_info.type;
995061da546Spatrick       return (this->*packet_callback)(packet_data.c_str());
996061da546Spatrick     } else {
997061da546Spatrick       // Do not fall through to end of this function, if we have valid
998061da546Spatrick       // packet_info and it has a NULL callback, then we need to respect
999061da546Spatrick       // that it may not want any response or anything to be done.
1000061da546Spatrick       return err;
1001061da546Spatrick     }
1002061da546Spatrick   }
1003061da546Spatrick   return rnb_err;
1004061da546Spatrick }
1005061da546Spatrick 
CommDataReceived(const std::string & new_data)1006061da546Spatrick void RNBRemote::CommDataReceived(const std::string &new_data) {
1007061da546Spatrick   //  DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called",
1008061da546Spatrick   //  (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
1009061da546Spatrick   {
1010061da546Spatrick     // Put the packet data into the buffer in a thread safe fashion
1011061da546Spatrick     PThreadMutex::Locker locker(m_mutex);
1012061da546Spatrick 
1013061da546Spatrick     std::string data;
1014061da546Spatrick     // See if we have any left over data from a previous call to this
1015061da546Spatrick     // function?
1016061da546Spatrick     if (!m_rx_partial_data.empty()) {
1017061da546Spatrick       // We do, so lets start with that data
1018061da546Spatrick       data.swap(m_rx_partial_data);
1019061da546Spatrick     }
1020061da546Spatrick     // Append the new incoming data
1021061da546Spatrick     data += new_data;
1022061da546Spatrick 
1023061da546Spatrick     // Parse up the packets into gdb remote packets
1024061da546Spatrick     size_t idx = 0;
1025061da546Spatrick     const size_t data_size = data.size();
1026061da546Spatrick 
1027061da546Spatrick     while (idx < data_size) {
1028061da546Spatrick       // end_idx must be one past the last valid packet byte. Start
1029061da546Spatrick       // it off with an invalid value that is the same as the current
1030061da546Spatrick       // index.
1031061da546Spatrick       size_t end_idx = idx;
1032061da546Spatrick 
1033061da546Spatrick       switch (data[idx]) {
1034061da546Spatrick       case '+':            // Look for ack
1035061da546Spatrick       case '-':            // Look for cancel
1036061da546Spatrick       case '\x03':         // ^C to halt target
1037061da546Spatrick         end_idx = idx + 1; // The command is one byte long...
1038061da546Spatrick         break;
1039061da546Spatrick 
1040061da546Spatrick       case '$':
1041061da546Spatrick         // Look for a standard gdb packet?
1042061da546Spatrick         end_idx = data.find('#', idx + 1);
1043061da546Spatrick         if (end_idx == std::string::npos || end_idx + 3 > data_size) {
1044061da546Spatrick           end_idx = std::string::npos;
1045061da546Spatrick         } else {
1046061da546Spatrick           // Add two for the checksum bytes and 1 to point to the
1047061da546Spatrick           // byte just past the end of this packet
1048061da546Spatrick           end_idx += 3;
1049061da546Spatrick         }
1050061da546Spatrick         break;
1051061da546Spatrick 
1052061da546Spatrick       default:
1053061da546Spatrick         break;
1054061da546Spatrick       }
1055061da546Spatrick 
1056061da546Spatrick       if (end_idx == std::string::npos) {
1057061da546Spatrick         // Not all data may be here for the packet yet, save it for
1058061da546Spatrick         // next time through this function.
1059061da546Spatrick         m_rx_partial_data += data.substr(idx);
1060061da546Spatrick         // DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s saving data for
1061061da546Spatrick         // later[%u, npos):
1062061da546Spatrick         // '%s'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
1063061da546Spatrick         // __FUNCTION__, idx, m_rx_partial_data.c_str());
1064061da546Spatrick         idx = end_idx;
1065061da546Spatrick       } else if (idx < end_idx) {
1066061da546Spatrick         m_packets_recvd++;
1067061da546Spatrick         // Hack to get rid of initial '+' ACK???
1068061da546Spatrick         if (m_packets_recvd == 1 && (end_idx == idx + 1) && data[idx] == '+') {
1069061da546Spatrick           // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s throwing first
1070061da546Spatrick           // ACK away....[%u, npos):
1071061da546Spatrick           // '+'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
1072061da546Spatrick           // __FUNCTION__, idx);
1073061da546Spatrick         } else {
1074061da546Spatrick           // We have a valid packet...
1075061da546Spatrick           m_rx_packets.push_back(data.substr(idx, end_idx - idx));
1076061da546Spatrick           DNBLogThreadedIf(LOG_RNB_PACKETS, "getpkt: %s",
1077061da546Spatrick                            m_rx_packets.back().c_str());
1078061da546Spatrick         }
1079061da546Spatrick         idx = end_idx;
1080061da546Spatrick       } else {
1081061da546Spatrick         DNBLogThreadedIf(LOG_RNB_MAX,
1082061da546Spatrick                          "%8d RNBRemote::%s tossing junk byte at %c",
1083061da546Spatrick                          (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
1084061da546Spatrick                          __FUNCTION__, data[idx]);
1085061da546Spatrick         idx = idx + 1;
1086061da546Spatrick       }
1087061da546Spatrick     }
1088061da546Spatrick   }
1089061da546Spatrick 
1090061da546Spatrick   if (!m_rx_packets.empty()) {
1091061da546Spatrick     // Let the main thread know we have received a packet
1092061da546Spatrick 
1093061da546Spatrick     // DNBLogThreadedIf (LOG_RNB_EVENTS, "%8d RNBRemote::%s   called
1094061da546Spatrick     // events.SetEvent(RNBContext::event_read_packet_available)",
1095061da546Spatrick     // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
1096061da546Spatrick     PThreadEvent &events = m_ctx.Events();
1097061da546Spatrick     events.SetEvents(RNBContext::event_read_packet_available);
1098061da546Spatrick   }
1099061da546Spatrick }
1100061da546Spatrick 
GetCommData()1101061da546Spatrick rnb_err_t RNBRemote::GetCommData() {
1102061da546Spatrick   //  DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called",
1103061da546Spatrick   //  (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
1104061da546Spatrick   std::string comm_data;
1105061da546Spatrick   rnb_err_t err = m_comm.Read(comm_data);
1106061da546Spatrick   if (err == rnb_success) {
1107061da546Spatrick     if (!comm_data.empty())
1108061da546Spatrick       CommDataReceived(comm_data);
1109061da546Spatrick   }
1110061da546Spatrick   return err;
1111061da546Spatrick }
1112061da546Spatrick 
StartReadRemoteDataThread()1113061da546Spatrick void RNBRemote::StartReadRemoteDataThread() {
1114061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called",
1115061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
1116061da546Spatrick                    __FUNCTION__);
1117061da546Spatrick   PThreadEvent &events = m_ctx.Events();
1118061da546Spatrick   if ((events.GetEventBits() & RNBContext::event_read_thread_running) == 0) {
1119061da546Spatrick     events.ResetEvents(RNBContext::event_read_thread_exiting);
1120061da546Spatrick     int err = ::pthread_create(&m_rx_pthread, NULL,
1121061da546Spatrick                                ThreadFunctionReadRemoteData, this);
1122061da546Spatrick     if (err == 0) {
1123061da546Spatrick       // Our thread was successfully kicked off, wait for it to
1124061da546Spatrick       // set the started event so we can safely continue
1125061da546Spatrick       events.WaitForSetEvents(RNBContext::event_read_thread_running);
1126061da546Spatrick     } else {
1127061da546Spatrick       events.ResetEvents(RNBContext::event_read_thread_running);
1128061da546Spatrick       events.SetEvents(RNBContext::event_read_thread_exiting);
1129061da546Spatrick     }
1130061da546Spatrick   }
1131061da546Spatrick }
1132061da546Spatrick 
StopReadRemoteDataThread()1133061da546Spatrick void RNBRemote::StopReadRemoteDataThread() {
1134061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called",
1135061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
1136061da546Spatrick                    __FUNCTION__);
1137061da546Spatrick   PThreadEvent &events = m_ctx.Events();
1138061da546Spatrick   if ((events.GetEventBits() & RNBContext::event_read_thread_running) ==
1139061da546Spatrick       RNBContext::event_read_thread_running) {
1140be691f3bSpatrick     DNBLog("debugserver about to shut down packet communications to lldb.");
1141061da546Spatrick     m_comm.Disconnect(true);
1142061da546Spatrick     struct timespec timeout_abstime;
1143061da546Spatrick     DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
1144061da546Spatrick 
1145061da546Spatrick     // Wait for 2 seconds for the remote data thread to exit
1146061da546Spatrick     if (events.WaitForSetEvents(RNBContext::event_read_thread_exiting,
1147061da546Spatrick                                 &timeout_abstime) == 0) {
1148061da546Spatrick       // Kill the remote data thread???
1149061da546Spatrick     }
1150061da546Spatrick   }
1151061da546Spatrick }
1152061da546Spatrick 
ThreadFunctionReadRemoteData(void * arg)1153061da546Spatrick void *RNBRemote::ThreadFunctionReadRemoteData(void *arg) {
1154061da546Spatrick   // Keep a shared pointer reference so this doesn't go away on us before the
1155061da546Spatrick   // thread is killed.
1156061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread starting...",
1157061da546Spatrick                    __FUNCTION__, arg);
1158061da546Spatrick   RNBRemoteSP remoteSP(g_remoteSP);
1159061da546Spatrick   if (remoteSP.get() != NULL) {
1160061da546Spatrick 
1161061da546Spatrick #if defined(__APPLE__)
1162061da546Spatrick     pthread_setname_np("read gdb-remote packets thread");
1163061da546Spatrick #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
1164061da546Spatrick     struct sched_param thread_param;
1165061da546Spatrick     int thread_sched_policy;
1166061da546Spatrick     if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
1167061da546Spatrick                               &thread_param) == 0) {
1168061da546Spatrick       thread_param.sched_priority = 47;
1169061da546Spatrick       pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
1170061da546Spatrick     }
1171061da546Spatrick #endif
1172061da546Spatrick #endif
1173061da546Spatrick 
1174061da546Spatrick     RNBRemote *remote = remoteSP.get();
1175061da546Spatrick     PThreadEvent &events = remote->Context().Events();
1176061da546Spatrick     events.SetEvents(RNBContext::event_read_thread_running);
1177061da546Spatrick     // START: main receive remote command thread loop
1178061da546Spatrick     bool done = false;
1179061da546Spatrick     while (!done) {
1180061da546Spatrick       rnb_err_t err = remote->GetCommData();
1181061da546Spatrick 
1182061da546Spatrick       switch (err) {
1183061da546Spatrick       case rnb_success:
1184061da546Spatrick         break;
1185061da546Spatrick 
1186061da546Spatrick       case rnb_err:
1187061da546Spatrick         DNBLogThreadedIf(LOG_RNB_REMOTE,
1188061da546Spatrick                          "RNBSocket::GetCommData returned error %u", err);
1189061da546Spatrick         done = true;
1190061da546Spatrick         break;
1191061da546Spatrick 
1192061da546Spatrick       case rnb_not_connected:
1193061da546Spatrick         DNBLogThreadedIf(LOG_RNB_REMOTE,
1194061da546Spatrick                          "RNBSocket::GetCommData returned not connected...");
1195061da546Spatrick         done = true;
1196061da546Spatrick         break;
1197061da546Spatrick       }
1198061da546Spatrick     }
1199061da546Spatrick     // START: main receive remote command thread loop
1200061da546Spatrick     events.ResetEvents(RNBContext::event_read_thread_running);
1201061da546Spatrick     events.SetEvents(RNBContext::event_read_thread_exiting);
1202061da546Spatrick   }
1203061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread exiting...",
1204061da546Spatrick                    __FUNCTION__, arg);
1205061da546Spatrick   return NULL;
1206061da546Spatrick }
1207061da546Spatrick 
1208061da546Spatrick // If we fail to get back a valid CPU type for the remote process,
1209061da546Spatrick // make a best guess for the CPU type based on the currently running
1210061da546Spatrick // debugserver binary -- the debugger may not handle the case of an
1211061da546Spatrick // un-specified process CPU type correctly.
1212061da546Spatrick 
best_guess_cpu_type()1213061da546Spatrick static cpu_type_t best_guess_cpu_type() {
1214061da546Spatrick #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
1215061da546Spatrick   if (sizeof(char *) == 8) {
1216061da546Spatrick     return CPU_TYPE_ARM64;
1217061da546Spatrick   } else {
1218061da546Spatrick #if defined (__ARM64_ARCH_8_32__)
1219061da546Spatrick     return CPU_TYPE_ARM64_32;
1220061da546Spatrick #endif
1221061da546Spatrick     return CPU_TYPE_ARM;
1222061da546Spatrick   }
1223061da546Spatrick #elif defined(__i386__) || defined(__x86_64__)
1224061da546Spatrick   if (sizeof(char *) == 8) {
1225061da546Spatrick     return CPU_TYPE_X86_64;
1226061da546Spatrick   } else {
1227061da546Spatrick     return CPU_TYPE_I386;
1228061da546Spatrick   }
1229061da546Spatrick #endif
1230061da546Spatrick   return 0;
1231061da546Spatrick }
1232061da546Spatrick 
1233061da546Spatrick /* Read the bytes in STR which are GDB Remote Protocol binary encoded bytes
1234061da546Spatrick  (8-bit bytes).
1235061da546Spatrick  This encoding uses 0x7d ('}') as an escape character for
1236061da546Spatrick  0x7d ('}'), 0x23 ('#'), 0x24 ('$'), 0x2a ('*').
1237061da546Spatrick  LEN is the number of bytes to be processed.  If a character is escaped,
1238061da546Spatrick  it is 2 characters for LEN.  A LEN of -1 means decode-until-nul-byte
1239061da546Spatrick  (end of string).  */
1240061da546Spatrick 
decode_binary_data(const char * str,size_t len)1241061da546Spatrick std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
1242061da546Spatrick   std::vector<uint8_t> bytes;
1243061da546Spatrick   if (len == 0) {
1244061da546Spatrick     return bytes;
1245061da546Spatrick   }
1246061da546Spatrick   if (len == (size_t)-1)
1247061da546Spatrick     len = strlen(str);
1248061da546Spatrick 
1249061da546Spatrick   while (len--) {
1250061da546Spatrick     unsigned char c = *str++;
1251061da546Spatrick     if (c == 0x7d && len > 0) {
1252061da546Spatrick       len--;
1253061da546Spatrick       c = *str++ ^ 0x20;
1254061da546Spatrick     }
1255061da546Spatrick     bytes.push_back(c);
1256061da546Spatrick   }
1257061da546Spatrick   return bytes;
1258061da546Spatrick }
1259061da546Spatrick 
1260061da546Spatrick // Quote any meta characters in a std::string as per the binary
1261061da546Spatrick // packet convention in the gdb-remote protocol.
1262061da546Spatrick 
binary_encode_string(const std::string & s)1263061da546Spatrick static std::string binary_encode_string(const std::string &s) {
1264061da546Spatrick   std::string output;
1265061da546Spatrick   const size_t s_size = s.size();
1266061da546Spatrick   const char *s_chars = s.c_str();
1267061da546Spatrick 
1268061da546Spatrick   for (size_t i = 0; i < s_size; i++) {
1269061da546Spatrick     unsigned char ch = *(s_chars + i);
1270061da546Spatrick     if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
1271061da546Spatrick       output.push_back('}'); // 0x7d
1272061da546Spatrick       output.push_back(ch ^ 0x20);
1273061da546Spatrick     } else {
1274061da546Spatrick       output.push_back(ch);
1275061da546Spatrick     }
1276061da546Spatrick   }
1277061da546Spatrick   return output;
1278061da546Spatrick }
1279061da546Spatrick 
1280061da546Spatrick // If the value side of a key-value pair in JSON is a string,
1281061da546Spatrick // and that string has a " character in it, the " character must
1282061da546Spatrick // be escaped.
1283061da546Spatrick 
json_string_quote_metachars(const std::string & s)1284061da546Spatrick std::string json_string_quote_metachars(const std::string &s) {
1285061da546Spatrick   if (s.find('"') == std::string::npos)
1286061da546Spatrick     return s;
1287061da546Spatrick 
1288061da546Spatrick   std::string output;
1289061da546Spatrick   const size_t s_size = s.size();
1290061da546Spatrick   const char *s_chars = s.c_str();
1291061da546Spatrick   for (size_t i = 0; i < s_size; i++) {
1292061da546Spatrick     unsigned char ch = *(s_chars + i);
1293061da546Spatrick     if (ch == '"') {
1294061da546Spatrick       output.push_back('\\');
1295061da546Spatrick     }
1296061da546Spatrick     output.push_back(ch);
1297061da546Spatrick   }
1298061da546Spatrick   return output;
1299061da546Spatrick }
1300061da546Spatrick 
1301061da546Spatrick typedef struct register_map_entry {
1302061da546Spatrick   uint32_t debugserver_regnum; // debugserver register number
1303061da546Spatrick   uint32_t offset; // Offset in bytes into the register context data with no
1304061da546Spatrick                    // padding between register values
1305061da546Spatrick   DNBRegisterInfo nub_info; // debugnub register info
1306061da546Spatrick   std::vector<uint32_t> value_regnums;
1307061da546Spatrick   std::vector<uint32_t> invalidate_regnums;
1308061da546Spatrick } register_map_entry_t;
1309061da546Spatrick 
1310061da546Spatrick // If the notion of registers differs from what is handed out by the
1311061da546Spatrick // architecture, then flavors can be defined here.
1312061da546Spatrick 
1313061da546Spatrick static std::vector<register_map_entry_t> g_dynamic_register_map;
1314061da546Spatrick static register_map_entry_t *g_reg_entries = NULL;
1315061da546Spatrick static size_t g_num_reg_entries = 0;
1316061da546Spatrick 
Initialize()1317061da546Spatrick void RNBRemote::Initialize() { DNBInitialize(); }
1318061da546Spatrick 
InitializeRegisters(bool force)1319061da546Spatrick bool RNBRemote::InitializeRegisters(bool force) {
1320061da546Spatrick   pid_t pid = m_ctx.ProcessID();
1321061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
1322061da546Spatrick     return false;
1323061da546Spatrick 
1324061da546Spatrick   DNBLogThreadedIf(
1325061da546Spatrick       LOG_RNB_PROC,
1326061da546Spatrick       "RNBRemote::%s() getting native registers from DNB interface",
1327061da546Spatrick       __FUNCTION__);
1328061da546Spatrick   // Discover the registers by querying the DNB interface and letting it
1329061da546Spatrick   // state the registers that it would like to export. This allows the
1330061da546Spatrick   // registers to be discovered using multiple qRegisterInfo calls to get
1331061da546Spatrick   // all register information after the architecture for the process is
1332061da546Spatrick   // determined.
1333061da546Spatrick   if (force) {
1334061da546Spatrick     g_dynamic_register_map.clear();
1335061da546Spatrick     g_reg_entries = NULL;
1336061da546Spatrick     g_num_reg_entries = 0;
1337061da546Spatrick   }
1338061da546Spatrick 
1339061da546Spatrick   if (g_dynamic_register_map.empty()) {
1340061da546Spatrick     nub_size_t num_reg_sets = 0;
1341061da546Spatrick     const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets);
1342061da546Spatrick 
1343061da546Spatrick     assert(num_reg_sets > 0 && reg_sets != NULL);
1344061da546Spatrick 
1345061da546Spatrick     uint32_t regnum = 0;
1346061da546Spatrick     uint32_t reg_data_offset = 0;
1347061da546Spatrick     typedef std::map<std::string, uint32_t> NameToRegNum;
1348061da546Spatrick     NameToRegNum name_to_regnum;
1349061da546Spatrick     for (nub_size_t set = 0; set < num_reg_sets; ++set) {
1350061da546Spatrick       if (reg_sets[set].registers == NULL)
1351061da546Spatrick         continue;
1352061da546Spatrick 
1353061da546Spatrick       for (uint32_t reg = 0; reg < reg_sets[set].num_registers; ++reg) {
1354061da546Spatrick         register_map_entry_t reg_entry = {
1355061da546Spatrick             regnum++, // register number starts at zero and goes up with no gaps
1356061da546Spatrick             reg_data_offset, // Offset into register context data, no gaps
1357061da546Spatrick                              // between registers
1358061da546Spatrick             reg_sets[set].registers[reg], // DNBRegisterInfo
1359061da546Spatrick             {},
1360061da546Spatrick             {},
1361061da546Spatrick         };
1362061da546Spatrick 
1363061da546Spatrick         name_to_regnum[reg_entry.nub_info.name] = reg_entry.debugserver_regnum;
1364061da546Spatrick 
1365061da546Spatrick         if (reg_entry.nub_info.value_regs == NULL) {
1366061da546Spatrick           reg_data_offset += reg_entry.nub_info.size;
1367061da546Spatrick         }
1368061da546Spatrick 
1369061da546Spatrick         g_dynamic_register_map.push_back(reg_entry);
1370061da546Spatrick       }
1371061da546Spatrick     }
1372061da546Spatrick 
1373061da546Spatrick     // Now we must find any registers whose values are in other registers and
1374061da546Spatrick     // fix up
1375061da546Spatrick     // the offsets since we removed all gaps...
1376061da546Spatrick     for (auto &reg_entry : g_dynamic_register_map) {
1377061da546Spatrick       if (reg_entry.nub_info.value_regs) {
1378061da546Spatrick         uint32_t new_offset = UINT32_MAX;
1379061da546Spatrick         for (size_t i = 0; reg_entry.nub_info.value_regs[i] != NULL; ++i) {
1380061da546Spatrick           const char *name = reg_entry.nub_info.value_regs[i];
1381061da546Spatrick           auto pos = name_to_regnum.find(name);
1382061da546Spatrick           if (pos != name_to_regnum.end()) {
1383061da546Spatrick             regnum = pos->second;
1384061da546Spatrick             reg_entry.value_regnums.push_back(regnum);
1385061da546Spatrick             if (regnum < g_dynamic_register_map.size()) {
1386061da546Spatrick               // The offset for value_regs registers is the offset within the
1387061da546Spatrick               // register with the lowest offset
1388061da546Spatrick               const uint32_t reg_offset =
1389061da546Spatrick                   g_dynamic_register_map[regnum].offset +
1390061da546Spatrick                   reg_entry.nub_info.offset;
1391061da546Spatrick               if (new_offset > reg_offset)
1392061da546Spatrick                 new_offset = reg_offset;
1393061da546Spatrick             }
1394061da546Spatrick           }
1395061da546Spatrick         }
1396061da546Spatrick 
1397061da546Spatrick         if (new_offset != UINT32_MAX) {
1398061da546Spatrick           reg_entry.offset = new_offset;
1399061da546Spatrick         } else {
1400061da546Spatrick           DNBLogThreaded("no offset was calculated entry for register %s",
1401061da546Spatrick                          reg_entry.nub_info.name);
1402061da546Spatrick           reg_entry.offset = UINT32_MAX;
1403061da546Spatrick         }
1404061da546Spatrick       }
1405061da546Spatrick 
1406061da546Spatrick       if (reg_entry.nub_info.update_regs) {
1407061da546Spatrick         for (size_t i = 0; reg_entry.nub_info.update_regs[i] != NULL; ++i) {
1408061da546Spatrick           const char *name = reg_entry.nub_info.update_regs[i];
1409061da546Spatrick           auto pos = name_to_regnum.find(name);
1410061da546Spatrick           if (pos != name_to_regnum.end()) {
1411061da546Spatrick             regnum = pos->second;
1412061da546Spatrick             reg_entry.invalidate_regnums.push_back(regnum);
1413061da546Spatrick           }
1414061da546Spatrick         }
1415061da546Spatrick       }
1416061da546Spatrick     }
1417061da546Spatrick 
1418061da546Spatrick     //        for (auto &reg_entry: g_dynamic_register_map)
1419061da546Spatrick     //        {
1420061da546Spatrick     //            DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s",
1421061da546Spatrick     //                           reg_entry.offset,
1422061da546Spatrick     //                           reg_entry.nub_info.size,
1423061da546Spatrick     //                           reg_entry.nub_info.value_regs != NULL,
1424061da546Spatrick     //                           reg_entry.nub_info.name);
1425061da546Spatrick     //        }
1426061da546Spatrick 
1427061da546Spatrick     g_reg_entries = g_dynamic_register_map.data();
1428061da546Spatrick     g_num_reg_entries = g_dynamic_register_map.size();
1429061da546Spatrick   }
1430061da546Spatrick   return true;
1431061da546Spatrick }
1432061da546Spatrick 
1433061da546Spatrick /* The inferior has stopped executing; send a packet
1434061da546Spatrick  to gdb to let it know.  */
1435061da546Spatrick 
NotifyThatProcessStopped(void)1436061da546Spatrick void RNBRemote::NotifyThatProcessStopped(void) {
1437061da546Spatrick   RNBRemote::HandlePacket_last_signal(NULL);
1438061da546Spatrick   return;
1439061da546Spatrick }
1440061da546Spatrick 
1441061da546Spatrick /* 'A arglen,argnum,arg,...'
1442061da546Spatrick  Update the inferior context CTX with the program name and arg
1443061da546Spatrick  list.
1444061da546Spatrick  The documentation for this packet is underwhelming but my best reading
1445061da546Spatrick  of this is that it is a series of (len, position #, arg)'s, one for
1446061da546Spatrick  each argument with "arg" hex encoded (two 0-9a-f chars?).
1447061da546Spatrick  Why we need BOTH a "len" and a hex encoded "arg" is beyond me - either
1448061da546Spatrick  is sufficient to get around the "," position separator escape issue.
1449061da546Spatrick 
1450061da546Spatrick  e.g. our best guess for a valid 'A' packet for "gdb -q a.out" is
1451061da546Spatrick 
1452061da546Spatrick  6,0,676462,4,1,2d71,10,2,612e6f7574
1453061da546Spatrick 
1454061da546Spatrick  Note that "argnum" and "arglen" are numbers in base 10.  Again, that's
1455061da546Spatrick  not documented either way but I'm assuming it's so.  */
1456061da546Spatrick 
HandlePacket_A(const char * p)1457061da546Spatrick rnb_err_t RNBRemote::HandlePacket_A(const char *p) {
1458061da546Spatrick   if (p == NULL || *p == '\0') {
1459061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1460061da546Spatrick                                   "Null packet for 'A' pkt");
1461061da546Spatrick   }
1462061da546Spatrick   p++;
1463061da546Spatrick   if (*p == '\0' || !isdigit(*p)) {
1464061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1465061da546Spatrick                                   "arglen not specified on 'A' pkt");
1466061da546Spatrick   }
1467061da546Spatrick 
1468061da546Spatrick   /* I promise I don't modify it anywhere in this function.  strtoul()'s
1469061da546Spatrick    2nd arg has to be non-const which makes it problematic to step
1470061da546Spatrick    through the string easily.  */
1471061da546Spatrick   char *buf = const_cast<char *>(p);
1472061da546Spatrick 
1473061da546Spatrick   RNBContext &ctx = Context();
1474061da546Spatrick 
1475061da546Spatrick   while (*buf != '\0') {
1476061da546Spatrick     unsigned long arglen, argnum;
1477061da546Spatrick     std::string arg;
1478061da546Spatrick     char *c;
1479061da546Spatrick 
1480061da546Spatrick     errno = 0;
1481061da546Spatrick     arglen = strtoul(buf, &c, 10);
1482061da546Spatrick     if (errno != 0 && arglen == 0) {
1483061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1484061da546Spatrick                                     "arglen not a number on 'A' pkt");
1485061da546Spatrick     }
1486061da546Spatrick     if (*c != ',') {
1487061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1488061da546Spatrick                                     "arglen not followed by comma on 'A' pkt");
1489061da546Spatrick     }
1490061da546Spatrick     buf = c + 1;
1491061da546Spatrick 
1492061da546Spatrick     errno = 0;
1493061da546Spatrick     argnum = strtoul(buf, &c, 10);
1494061da546Spatrick     if (errno != 0 && argnum == 0) {
1495061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1496061da546Spatrick                                     "argnum not a number on 'A' pkt");
1497061da546Spatrick     }
1498061da546Spatrick     if (*c != ',') {
1499061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1500061da546Spatrick                                     "arglen not followed by comma on 'A' pkt");
1501061da546Spatrick     }
1502061da546Spatrick     buf = c + 1;
1503061da546Spatrick 
1504061da546Spatrick     c = buf;
1505061da546Spatrick     buf = buf + arglen;
1506061da546Spatrick     while (c < buf && *c != '\0' && c + 1 < buf && *(c + 1) != '\0') {
1507061da546Spatrick       char smallbuf[3];
1508061da546Spatrick       smallbuf[0] = *c;
1509061da546Spatrick       smallbuf[1] = *(c + 1);
1510061da546Spatrick       smallbuf[2] = '\0';
1511061da546Spatrick 
1512061da546Spatrick       errno = 0;
1513061da546Spatrick       int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
1514061da546Spatrick       if (errno != 0 && ch == 0) {
1515061da546Spatrick         return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1516061da546Spatrick                                       "non-hex char in arg on 'A' pkt");
1517061da546Spatrick       }
1518061da546Spatrick 
1519061da546Spatrick       arg.push_back(ch);
1520061da546Spatrick       c += 2;
1521061da546Spatrick     }
1522061da546Spatrick 
1523061da546Spatrick     ctx.PushArgument(arg.c_str());
1524061da546Spatrick     if (*buf == ',')
1525061da546Spatrick       buf++;
1526061da546Spatrick   }
1527061da546Spatrick   SendPacket("OK");
1528061da546Spatrick 
1529061da546Spatrick   return rnb_success;
1530061da546Spatrick }
1531061da546Spatrick 
1532061da546Spatrick /* 'H c t'
1533061da546Spatrick  Set the thread for subsequent actions; 'c' for step/continue ops,
1534061da546Spatrick  'g' for other ops.  -1 means all threads, 0 means any thread.  */
1535061da546Spatrick 
HandlePacket_H(const char * p)1536061da546Spatrick rnb_err_t RNBRemote::HandlePacket_H(const char *p) {
1537061da546Spatrick   p++; // skip 'H'
1538061da546Spatrick   if (*p != 'c' && *p != 'g') {
1539061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1540061da546Spatrick                                   "Missing 'c' or 'g' type in H packet");
1541061da546Spatrick   }
1542061da546Spatrick 
1543061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
1544061da546Spatrick     // We allow gdb to connect to a server that hasn't started running
1545061da546Spatrick     // the target yet.  gdb still wants to ask questions about it and
1546061da546Spatrick     // freaks out if it gets an error.  So just return OK here.
1547061da546Spatrick   }
1548061da546Spatrick 
1549061da546Spatrick   errno = 0;
1550061da546Spatrick   nub_thread_t tid = strtoul(p + 1, NULL, 16);
1551061da546Spatrick   if (errno != 0 && tid == 0) {
1552061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1553061da546Spatrick                                   "Invalid thread number in H packet");
1554061da546Spatrick   }
1555061da546Spatrick   if (*p == 'c')
1556061da546Spatrick     SetContinueThread(tid);
1557061da546Spatrick   if (*p == 'g')
1558061da546Spatrick     SetCurrentThread(tid);
1559061da546Spatrick 
1560061da546Spatrick   return SendPacket("OK");
1561061da546Spatrick }
1562061da546Spatrick 
HandlePacket_qLaunchSuccess(const char * p)1563061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qLaunchSuccess(const char *p) {
1564061da546Spatrick   if (m_ctx.HasValidProcessID() || m_ctx.LaunchStatus().Status() == 0)
1565061da546Spatrick     return SendPacket("OK");
1566061da546Spatrick   std::ostringstream ret_str;
1567061da546Spatrick   std::string status_str;
1568dda28197Spatrick   std::string error_quoted = binary_encode_string
1569dda28197Spatrick                (m_ctx.LaunchStatusAsString(status_str));
1570dda28197Spatrick   ret_str << "E" << error_quoted;
1571061da546Spatrick 
1572061da546Spatrick   return SendPacket(ret_str.str());
1573061da546Spatrick }
1574061da546Spatrick 
HandlePacket_qShlibInfoAddr(const char * p)1575061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qShlibInfoAddr(const char *p) {
1576061da546Spatrick   if (m_ctx.HasValidProcessID()) {
1577061da546Spatrick     nub_addr_t shlib_info_addr =
1578061da546Spatrick         DNBProcessGetSharedLibraryInfoAddress(m_ctx.ProcessID());
1579061da546Spatrick     if (shlib_info_addr != INVALID_NUB_ADDRESS) {
1580061da546Spatrick       std::ostringstream ostrm;
1581061da546Spatrick       ostrm << RAW_HEXBASE << shlib_info_addr;
1582061da546Spatrick       return SendPacket(ostrm.str());
1583061da546Spatrick     }
1584061da546Spatrick   }
1585061da546Spatrick   return SendPacket("E44");
1586061da546Spatrick }
1587061da546Spatrick 
HandlePacket_qStepPacketSupported(const char * p)1588061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qStepPacketSupported(const char *p) {
1589061da546Spatrick   // Normally the "s" packet is mandatory, yet in gdb when using ARM, they
1590061da546Spatrick   // get around the need for this packet by implementing software single
1591061da546Spatrick   // stepping from gdb. Current versions of debugserver do support the "s"
1592061da546Spatrick   // packet, yet some older versions do not. We need a way to tell if this
1593061da546Spatrick   // packet is supported so we can disable software single stepping in gdb
1594061da546Spatrick   // for remote targets (so the "s" packet will get used).
1595061da546Spatrick   return SendPacket("OK");
1596061da546Spatrick }
1597061da546Spatrick 
HandlePacket_qSyncThreadStateSupported(const char * p)1598061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qSyncThreadStateSupported(const char *p) {
1599061da546Spatrick   // We support attachOrWait meaning attach if the process exists, otherwise
1600061da546Spatrick   // wait to attach.
1601061da546Spatrick   return SendPacket("OK");
1602061da546Spatrick }
1603061da546Spatrick 
HandlePacket_qVAttachOrWaitSupported(const char * p)1604061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qVAttachOrWaitSupported(const char *p) {
1605061da546Spatrick   // We support attachOrWait meaning attach if the process exists, otherwise
1606061da546Spatrick   // wait to attach.
1607061da546Spatrick   return SendPacket("OK");
1608061da546Spatrick }
1609061da546Spatrick 
HandlePacket_qThreadStopInfo(const char * p)1610061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qThreadStopInfo(const char *p) {
1611061da546Spatrick   p += strlen("qThreadStopInfo");
1612061da546Spatrick   nub_thread_t tid = strtoul(p, 0, 16);
1613061da546Spatrick   return SendStopReplyPacketForThread(tid);
1614061da546Spatrick }
1615061da546Spatrick 
HandlePacket_qThreadInfo(const char * p)1616061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qThreadInfo(const char *p) {
1617061da546Spatrick   // We allow gdb to connect to a server that hasn't started running
1618061da546Spatrick   // the target yet.  gdb still wants to ask questions about it and
1619061da546Spatrick   // freaks out if it gets an error.  So just return OK here.
1620061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
1621061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
1622061da546Spatrick     return SendPacket("OK");
1623061da546Spatrick 
1624061da546Spatrick   // Only "qfThreadInfo" and "qsThreadInfo" get into this function so
1625061da546Spatrick   // we only need to check the second byte to tell which is which
1626061da546Spatrick   if (p[1] == 'f') {
1627061da546Spatrick     nub_size_t numthreads = DNBProcessGetNumThreads(pid);
1628061da546Spatrick     std::ostringstream ostrm;
1629061da546Spatrick     ostrm << "m";
1630061da546Spatrick     bool first = true;
1631061da546Spatrick     for (nub_size_t i = 0; i < numthreads; ++i) {
1632061da546Spatrick       if (first)
1633061da546Spatrick         first = false;
1634061da546Spatrick       else
1635061da546Spatrick         ostrm << ",";
1636061da546Spatrick       nub_thread_t th = DNBProcessGetThreadAtIndex(pid, i);
1637061da546Spatrick       ostrm << std::hex << th;
1638061da546Spatrick     }
1639061da546Spatrick     return SendPacket(ostrm.str());
1640061da546Spatrick   } else {
1641061da546Spatrick     return SendPacket("l");
1642061da546Spatrick   }
1643061da546Spatrick }
1644061da546Spatrick 
HandlePacket_qThreadExtraInfo(const char * p)1645061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qThreadExtraInfo(const char *p) {
1646061da546Spatrick   // We allow gdb to connect to a server that hasn't started running
1647061da546Spatrick   // the target yet.  gdb still wants to ask questions about it and
1648061da546Spatrick   // freaks out if it gets an error.  So just return OK here.
1649061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
1650061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
1651061da546Spatrick     return SendPacket("OK");
1652061da546Spatrick 
1653061da546Spatrick   /* This is supposed to return a string like 'Runnable' or
1654061da546Spatrick    'Blocked on Mutex'.
1655061da546Spatrick    The returned string is formatted like the "A" packet - a
1656061da546Spatrick    sequence of letters encoded in as 2-hex-chars-per-letter.  */
1657061da546Spatrick   p += strlen("qThreadExtraInfo");
1658061da546Spatrick   if (*p++ != ',')
1659061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1660061da546Spatrick                                   "Illformed qThreadExtraInfo packet");
1661061da546Spatrick   errno = 0;
1662061da546Spatrick   nub_thread_t tid = strtoul(p, NULL, 16);
1663061da546Spatrick   if (errno != 0 && tid == 0) {
1664061da546Spatrick     return HandlePacket_ILLFORMED(
1665061da546Spatrick         __FILE__, __LINE__, p,
1666061da546Spatrick         "Invalid thread number in qThreadExtraInfo packet");
1667061da546Spatrick   }
1668061da546Spatrick 
1669061da546Spatrick   const char *threadInfo = DNBThreadGetInfo(pid, tid);
1670061da546Spatrick   if (threadInfo != NULL && threadInfo[0]) {
1671061da546Spatrick     return SendHexEncodedBytePacket(NULL, threadInfo, strlen(threadInfo), NULL);
1672061da546Spatrick   } else {
1673061da546Spatrick     // "OK" == 4f6b
1674061da546Spatrick     // Return "OK" as a ASCII hex byte stream if things go wrong
1675061da546Spatrick     return SendPacket("4f6b");
1676061da546Spatrick   }
1677061da546Spatrick 
1678061da546Spatrick   return SendPacket("");
1679061da546Spatrick }
1680061da546Spatrick 
1681061da546Spatrick const char *k_space_delimiters = " \t";
skip_spaces(std::string & line)1682061da546Spatrick static void skip_spaces(std::string &line) {
1683061da546Spatrick   if (!line.empty()) {
1684061da546Spatrick     size_t space_pos = line.find_first_not_of(k_space_delimiters);
1685061da546Spatrick     if (space_pos > 0)
1686061da546Spatrick       line.erase(0, space_pos);
1687061da546Spatrick   }
1688061da546Spatrick }
1689061da546Spatrick 
get_identifier(std::string & line)1690061da546Spatrick static std::string get_identifier(std::string &line) {
1691061da546Spatrick   std::string word;
1692061da546Spatrick   skip_spaces(line);
1693061da546Spatrick   const size_t line_size = line.size();
1694061da546Spatrick   size_t end_pos;
1695061da546Spatrick   for (end_pos = 0; end_pos < line_size; ++end_pos) {
1696061da546Spatrick     if (end_pos == 0) {
1697061da546Spatrick       if (isalpha(line[end_pos]) || line[end_pos] == '_')
1698061da546Spatrick         continue;
1699061da546Spatrick     } else if (isalnum(line[end_pos]) || line[end_pos] == '_')
1700061da546Spatrick       continue;
1701061da546Spatrick     break;
1702061da546Spatrick   }
1703061da546Spatrick   word.assign(line, 0, end_pos);
1704061da546Spatrick   line.erase(0, end_pos);
1705061da546Spatrick   return word;
1706061da546Spatrick }
1707061da546Spatrick 
get_operator(std::string & line)1708061da546Spatrick static std::string get_operator(std::string &line) {
1709061da546Spatrick   std::string op;
1710061da546Spatrick   skip_spaces(line);
1711061da546Spatrick   if (!line.empty()) {
1712061da546Spatrick     if (line[0] == '=') {
1713061da546Spatrick       op = '=';
1714061da546Spatrick       line.erase(0, 1);
1715061da546Spatrick     }
1716061da546Spatrick   }
1717061da546Spatrick   return op;
1718061da546Spatrick }
1719061da546Spatrick 
get_value(std::string & line)1720061da546Spatrick static std::string get_value(std::string &line) {
1721061da546Spatrick   std::string value;
1722061da546Spatrick   skip_spaces(line);
1723061da546Spatrick   if (!line.empty()) {
1724061da546Spatrick     value.swap(line);
1725061da546Spatrick   }
1726061da546Spatrick   return value;
1727061da546Spatrick }
1728061da546Spatrick 
1729061da546Spatrick extern void FileLogCallback(void *baton, uint32_t flags, const char *format,
1730061da546Spatrick                             va_list args);
1731061da546Spatrick extern void ASLLogCallback(void *baton, uint32_t flags, const char *format,
1732061da546Spatrick                            va_list args);
1733061da546Spatrick 
HandlePacket_qRcmd(const char * p)1734061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qRcmd(const char *p) {
1735061da546Spatrick   const char *c = p + strlen("qRcmd,");
1736061da546Spatrick   std::string line;
1737061da546Spatrick   while (c[0] && c[1]) {
1738061da546Spatrick     char smallbuf[3] = {c[0], c[1], '\0'};
1739061da546Spatrick     errno = 0;
1740061da546Spatrick     int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
1741061da546Spatrick     if (errno != 0 && ch == 0)
1742061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
1743061da546Spatrick                                     "non-hex char in payload of qRcmd packet");
1744061da546Spatrick     line.push_back(ch);
1745061da546Spatrick     c += 2;
1746061da546Spatrick   }
1747061da546Spatrick   if (*c == '\0') {
1748061da546Spatrick     std::string command = get_identifier(line);
1749061da546Spatrick     if (command == "set") {
1750061da546Spatrick       std::string variable = get_identifier(line);
1751061da546Spatrick       std::string op = get_operator(line);
1752061da546Spatrick       std::string value = get_value(line);
1753061da546Spatrick       if (variable == "logfile") {
1754061da546Spatrick         FILE *log_file = fopen(value.c_str(), "w");
1755061da546Spatrick         if (log_file) {
1756061da546Spatrick           DNBLogSetLogCallback(FileLogCallback, log_file);
1757061da546Spatrick           return SendPacket("OK");
1758061da546Spatrick         }
1759061da546Spatrick         return SendPacket("E71");
1760061da546Spatrick       } else if (variable == "logmask") {
1761061da546Spatrick         char *end;
1762061da546Spatrick         errno = 0;
1763061da546Spatrick         uint32_t logmask =
1764061da546Spatrick             static_cast<uint32_t>(strtoul(value.c_str(), &end, 0));
1765061da546Spatrick         if (errno == 0 && end && *end == '\0') {
1766061da546Spatrick           DNBLogSetLogMask(logmask);
1767061da546Spatrick           if (!DNBLogGetLogCallback())
1768061da546Spatrick             DNBLogSetLogCallback(ASLLogCallback, NULL);
1769061da546Spatrick           return SendPacket("OK");
1770061da546Spatrick         }
1771061da546Spatrick         errno = 0;
1772061da546Spatrick         logmask = static_cast<uint32_t>(strtoul(value.c_str(), &end, 16));
1773061da546Spatrick         if (errno == 0 && end && *end == '\0') {
1774061da546Spatrick           DNBLogSetLogMask(logmask);
1775061da546Spatrick           return SendPacket("OK");
1776061da546Spatrick         }
1777061da546Spatrick         return SendPacket("E72");
1778061da546Spatrick       }
1779061da546Spatrick       return SendPacket("E70");
1780061da546Spatrick     }
1781061da546Spatrick     return SendPacket("E69");
1782061da546Spatrick   }
1783061da546Spatrick   return SendPacket("E73");
1784061da546Spatrick }
1785061da546Spatrick 
HandlePacket_qC(const char * p)1786061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qC(const char *p) {
1787061da546Spatrick   nub_thread_t tid;
1788061da546Spatrick   std::ostringstream rep;
1789061da546Spatrick   // If we haven't run the process yet, we tell the debugger the
1790061da546Spatrick   // pid is 0.  That way it can know to tell use to run later on.
1791061da546Spatrick   if (!m_ctx.HasValidProcessID())
1792061da546Spatrick     tid = 0;
1793061da546Spatrick   else {
1794061da546Spatrick     // Grab the current thread.
1795061da546Spatrick     tid = DNBProcessGetCurrentThread(m_ctx.ProcessID());
1796061da546Spatrick     // Make sure we set the current thread so g and p packets return
1797061da546Spatrick     // the data the gdb will expect.
1798061da546Spatrick     SetCurrentThread(tid);
1799061da546Spatrick   }
1800061da546Spatrick   rep << "QC" << std::hex << tid;
1801061da546Spatrick   return SendPacket(rep.str());
1802061da546Spatrick }
1803061da546Spatrick 
HandlePacket_qEcho(const char * p)1804061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qEcho(const char *p) {
1805061da546Spatrick   // Just send the exact same packet back that we received to
1806061da546Spatrick   // synchronize the response packets after a previous packet
1807061da546Spatrick   // timed out. This allows the debugger to get back on track
1808061da546Spatrick   // with responses after a packet timeout.
1809061da546Spatrick   return SendPacket(p);
1810061da546Spatrick }
1811061da546Spatrick 
HandlePacket_qGetPid(const char * p)1812061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qGetPid(const char *p) {
1813061da546Spatrick   nub_process_t pid;
1814061da546Spatrick   std::ostringstream rep;
1815061da546Spatrick   // If we haven't run the process yet, we tell the debugger the
1816061da546Spatrick   // pid is 0.  That way it can know to tell use to run later on.
1817061da546Spatrick   if (m_ctx.HasValidProcessID())
1818061da546Spatrick     pid = m_ctx.ProcessID();
1819061da546Spatrick   else
1820061da546Spatrick     pid = 0;
1821061da546Spatrick   rep << std::hex << pid;
1822061da546Spatrick   return SendPacket(rep.str());
1823061da546Spatrick }
1824061da546Spatrick 
HandlePacket_qRegisterInfo(const char * p)1825061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qRegisterInfo(const char *p) {
1826061da546Spatrick   if (g_num_reg_entries == 0)
1827061da546Spatrick     InitializeRegisters();
1828061da546Spatrick 
1829061da546Spatrick   p += strlen("qRegisterInfo");
1830061da546Spatrick 
1831061da546Spatrick   nub_size_t num_reg_sets = 0;
1832061da546Spatrick   const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo(&num_reg_sets);
1833061da546Spatrick   uint32_t reg_num = static_cast<uint32_t>(strtoul(p, 0, 16));
1834061da546Spatrick 
1835061da546Spatrick   if (reg_num < g_num_reg_entries) {
1836061da546Spatrick     const register_map_entry_t *reg_entry = &g_reg_entries[reg_num];
1837061da546Spatrick     std::ostringstream ostrm;
1838061da546Spatrick     if (reg_entry->nub_info.name)
1839061da546Spatrick       ostrm << "name:" << reg_entry->nub_info.name << ';';
1840061da546Spatrick     if (reg_entry->nub_info.alt)
1841061da546Spatrick       ostrm << "alt-name:" << reg_entry->nub_info.alt << ';';
1842061da546Spatrick 
1843061da546Spatrick     ostrm << "bitsize:" << std::dec << reg_entry->nub_info.size * 8 << ';';
1844061da546Spatrick     ostrm << "offset:" << std::dec << reg_entry->offset << ';';
1845061da546Spatrick 
1846061da546Spatrick     switch (reg_entry->nub_info.type) {
1847061da546Spatrick     case Uint:
1848061da546Spatrick       ostrm << "encoding:uint;";
1849061da546Spatrick       break;
1850061da546Spatrick     case Sint:
1851061da546Spatrick       ostrm << "encoding:sint;";
1852061da546Spatrick       break;
1853061da546Spatrick     case IEEE754:
1854061da546Spatrick       ostrm << "encoding:ieee754;";
1855061da546Spatrick       break;
1856061da546Spatrick     case Vector:
1857061da546Spatrick       ostrm << "encoding:vector;";
1858061da546Spatrick       break;
1859061da546Spatrick     }
1860061da546Spatrick 
1861061da546Spatrick     switch (reg_entry->nub_info.format) {
1862061da546Spatrick     case Binary:
1863061da546Spatrick       ostrm << "format:binary;";
1864061da546Spatrick       break;
1865061da546Spatrick     case Decimal:
1866061da546Spatrick       ostrm << "format:decimal;";
1867061da546Spatrick       break;
1868061da546Spatrick     case Hex:
1869061da546Spatrick       ostrm << "format:hex;";
1870061da546Spatrick       break;
1871061da546Spatrick     case Float:
1872061da546Spatrick       ostrm << "format:float;";
1873061da546Spatrick       break;
1874061da546Spatrick     case VectorOfSInt8:
1875061da546Spatrick       ostrm << "format:vector-sint8;";
1876061da546Spatrick       break;
1877061da546Spatrick     case VectorOfUInt8:
1878061da546Spatrick       ostrm << "format:vector-uint8;";
1879061da546Spatrick       break;
1880061da546Spatrick     case VectorOfSInt16:
1881061da546Spatrick       ostrm << "format:vector-sint16;";
1882061da546Spatrick       break;
1883061da546Spatrick     case VectorOfUInt16:
1884061da546Spatrick       ostrm << "format:vector-uint16;";
1885061da546Spatrick       break;
1886061da546Spatrick     case VectorOfSInt32:
1887061da546Spatrick       ostrm << "format:vector-sint32;";
1888061da546Spatrick       break;
1889061da546Spatrick     case VectorOfUInt32:
1890061da546Spatrick       ostrm << "format:vector-uint32;";
1891061da546Spatrick       break;
1892061da546Spatrick     case VectorOfFloat32:
1893061da546Spatrick       ostrm << "format:vector-float32;";
1894061da546Spatrick       break;
1895061da546Spatrick     case VectorOfUInt128:
1896061da546Spatrick       ostrm << "format:vector-uint128;";
1897061da546Spatrick       break;
1898061da546Spatrick     };
1899061da546Spatrick 
1900061da546Spatrick     if (reg_set_info && reg_entry->nub_info.set < num_reg_sets)
1901061da546Spatrick       ostrm << "set:" << reg_set_info[reg_entry->nub_info.set].name << ';';
1902061da546Spatrick 
1903061da546Spatrick     if (reg_entry->nub_info.reg_ehframe != INVALID_NUB_REGNUM)
1904061da546Spatrick       ostrm << "ehframe:" << std::dec << reg_entry->nub_info.reg_ehframe << ';';
1905061da546Spatrick 
1906061da546Spatrick     if (reg_entry->nub_info.reg_dwarf != INVALID_NUB_REGNUM)
1907061da546Spatrick       ostrm << "dwarf:" << std::dec << reg_entry->nub_info.reg_dwarf << ';';
1908061da546Spatrick 
1909061da546Spatrick     switch (reg_entry->nub_info.reg_generic) {
1910061da546Spatrick     case GENERIC_REGNUM_FP:
1911061da546Spatrick       ostrm << "generic:fp;";
1912061da546Spatrick       break;
1913061da546Spatrick     case GENERIC_REGNUM_PC:
1914061da546Spatrick       ostrm << "generic:pc;";
1915061da546Spatrick       break;
1916061da546Spatrick     case GENERIC_REGNUM_SP:
1917061da546Spatrick       ostrm << "generic:sp;";
1918061da546Spatrick       break;
1919061da546Spatrick     case GENERIC_REGNUM_RA:
1920061da546Spatrick       ostrm << "generic:ra;";
1921061da546Spatrick       break;
1922061da546Spatrick     case GENERIC_REGNUM_FLAGS:
1923061da546Spatrick       ostrm << "generic:flags;";
1924061da546Spatrick       break;
1925061da546Spatrick     case GENERIC_REGNUM_ARG1:
1926061da546Spatrick       ostrm << "generic:arg1;";
1927061da546Spatrick       break;
1928061da546Spatrick     case GENERIC_REGNUM_ARG2:
1929061da546Spatrick       ostrm << "generic:arg2;";
1930061da546Spatrick       break;
1931061da546Spatrick     case GENERIC_REGNUM_ARG3:
1932061da546Spatrick       ostrm << "generic:arg3;";
1933061da546Spatrick       break;
1934061da546Spatrick     case GENERIC_REGNUM_ARG4:
1935061da546Spatrick       ostrm << "generic:arg4;";
1936061da546Spatrick       break;
1937061da546Spatrick     case GENERIC_REGNUM_ARG5:
1938061da546Spatrick       ostrm << "generic:arg5;";
1939061da546Spatrick       break;
1940061da546Spatrick     case GENERIC_REGNUM_ARG6:
1941061da546Spatrick       ostrm << "generic:arg6;";
1942061da546Spatrick       break;
1943061da546Spatrick     case GENERIC_REGNUM_ARG7:
1944061da546Spatrick       ostrm << "generic:arg7;";
1945061da546Spatrick       break;
1946061da546Spatrick     case GENERIC_REGNUM_ARG8:
1947061da546Spatrick       ostrm << "generic:arg8;";
1948061da546Spatrick       break;
1949061da546Spatrick     default:
1950061da546Spatrick       break;
1951061da546Spatrick     }
1952061da546Spatrick 
1953061da546Spatrick     if (!reg_entry->value_regnums.empty()) {
1954061da546Spatrick       ostrm << "container-regs:";
1955061da546Spatrick       for (size_t i = 0, n = reg_entry->value_regnums.size(); i < n; ++i) {
1956061da546Spatrick         if (i > 0)
1957061da546Spatrick           ostrm << ',';
1958061da546Spatrick         ostrm << RAW_HEXBASE << reg_entry->value_regnums[i];
1959061da546Spatrick       }
1960061da546Spatrick       ostrm << ';';
1961061da546Spatrick     }
1962061da546Spatrick 
1963061da546Spatrick     if (!reg_entry->invalidate_regnums.empty()) {
1964061da546Spatrick       ostrm << "invalidate-regs:";
1965061da546Spatrick       for (size_t i = 0, n = reg_entry->invalidate_regnums.size(); i < n; ++i) {
1966061da546Spatrick         if (i > 0)
1967061da546Spatrick           ostrm << ',';
1968061da546Spatrick         ostrm << RAW_HEXBASE << reg_entry->invalidate_regnums[i];
1969061da546Spatrick       }
1970061da546Spatrick       ostrm << ';';
1971061da546Spatrick     }
1972061da546Spatrick 
1973061da546Spatrick     return SendPacket(ostrm.str());
1974061da546Spatrick   }
1975061da546Spatrick   return SendPacket("E45");
1976061da546Spatrick }
1977061da546Spatrick 
1978061da546Spatrick /* This expects a packet formatted like
1979061da546Spatrick 
1980061da546Spatrick  QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE;
1981061da546Spatrick 
1982061da546Spatrick  with the "QSetLogging:" already removed from the start.  Maybe in the
1983061da546Spatrick  future this packet will include other keyvalue pairs like
1984061da546Spatrick 
1985061da546Spatrick  QSetLogging:bitmask=LOG_ALL;mode=asl;
1986061da546Spatrick  */
1987061da546Spatrick 
set_logging(const char * p)1988061da546Spatrick rnb_err_t set_logging(const char *p) {
1989061da546Spatrick   int bitmask = 0;
1990061da546Spatrick   while (p && *p != '\0') {
1991061da546Spatrick     if (strncmp(p, "bitmask=", sizeof("bitmask=") - 1) == 0) {
1992061da546Spatrick       p += sizeof("bitmask=") - 1;
1993061da546Spatrick       while (p && *p != '\0' && *p != ';') {
1994061da546Spatrick         if (*p == '|')
1995061da546Spatrick           p++;
1996061da546Spatrick 
1997061da546Spatrick         // to regenerate the LOG_ entries (not including the LOG_RNB entries)
1998061da546Spatrick         // $ for logname in `grep '^#define LOG_' DNBDefs.h | egrep -v
1999061da546Spatrick         // 'LOG_HI|LOG_LO' | awk '{print $2}'`
2000061da546Spatrick         // do
2001061da546Spatrick         //   echo "                else if (strncmp (p, \"$logname\", sizeof
2002061da546Spatrick         //   (\"$logname\") - 1) == 0)"
2003061da546Spatrick         //   echo "                {"
2004061da546Spatrick         //   echo "                    p += sizeof (\"$logname\") - 1;"
2005061da546Spatrick         //   echo "                    bitmask |= $logname;"
2006061da546Spatrick         //   echo "                }"
2007061da546Spatrick         // done
2008061da546Spatrick         if (strncmp(p, "LOG_VERBOSE", sizeof("LOG_VERBOSE") - 1) == 0) {
2009061da546Spatrick           p += sizeof("LOG_VERBOSE") - 1;
2010061da546Spatrick           bitmask |= LOG_VERBOSE;
2011061da546Spatrick         } else if (strncmp(p, "LOG_PROCESS", sizeof("LOG_PROCESS") - 1) == 0) {
2012061da546Spatrick           p += sizeof("LOG_PROCESS") - 1;
2013061da546Spatrick           bitmask |= LOG_PROCESS;
2014061da546Spatrick         } else if (strncmp(p, "LOG_THREAD", sizeof("LOG_THREAD") - 1) == 0) {
2015061da546Spatrick           p += sizeof("LOG_THREAD") - 1;
2016061da546Spatrick           bitmask |= LOG_THREAD;
2017061da546Spatrick         } else if (strncmp(p, "LOG_EXCEPTIONS", sizeof("LOG_EXCEPTIONS") - 1) ==
2018061da546Spatrick                    0) {
2019061da546Spatrick           p += sizeof("LOG_EXCEPTIONS") - 1;
2020061da546Spatrick           bitmask |= LOG_EXCEPTIONS;
2021061da546Spatrick         } else if (strncmp(p, "LOG_SHLIB", sizeof("LOG_SHLIB") - 1) == 0) {
2022061da546Spatrick           p += sizeof("LOG_SHLIB") - 1;
2023061da546Spatrick           bitmask |= LOG_SHLIB;
2024061da546Spatrick         } else if (strncmp(p, "LOG_MEMORY_DATA_SHORT",
2025061da546Spatrick                            sizeof("LOG_MEMORY_DATA_SHORT") - 1) == 0) {
2026061da546Spatrick           p += sizeof("LOG_MEMORY_DATA_SHORT") - 1;
2027061da546Spatrick           bitmask |= LOG_MEMORY_DATA_SHORT;
2028061da546Spatrick         } else if (strncmp(p, "LOG_MEMORY_DATA_LONG",
2029061da546Spatrick                            sizeof("LOG_MEMORY_DATA_LONG") - 1) == 0) {
2030061da546Spatrick           p += sizeof("LOG_MEMORY_DATA_LONG") - 1;
2031061da546Spatrick           bitmask |= LOG_MEMORY_DATA_LONG;
2032061da546Spatrick         } else if (strncmp(p, "LOG_MEMORY_PROTECTIONS",
2033061da546Spatrick                            sizeof("LOG_MEMORY_PROTECTIONS") - 1) == 0) {
2034061da546Spatrick           p += sizeof("LOG_MEMORY_PROTECTIONS") - 1;
2035061da546Spatrick           bitmask |= LOG_MEMORY_PROTECTIONS;
2036061da546Spatrick         } else if (strncmp(p, "LOG_MEMORY", sizeof("LOG_MEMORY") - 1) == 0) {
2037061da546Spatrick           p += sizeof("LOG_MEMORY") - 1;
2038061da546Spatrick           bitmask |= LOG_MEMORY;
2039061da546Spatrick         } else if (strncmp(p, "LOG_BREAKPOINTS",
2040061da546Spatrick                            sizeof("LOG_BREAKPOINTS") - 1) == 0) {
2041061da546Spatrick           p += sizeof("LOG_BREAKPOINTS") - 1;
2042061da546Spatrick           bitmask |= LOG_BREAKPOINTS;
2043061da546Spatrick         } else if (strncmp(p, "LOG_EVENTS", sizeof("LOG_EVENTS") - 1) == 0) {
2044061da546Spatrick           p += sizeof("LOG_EVENTS") - 1;
2045061da546Spatrick           bitmask |= LOG_EVENTS;
2046061da546Spatrick         } else if (strncmp(p, "LOG_WATCHPOINTS",
2047061da546Spatrick                            sizeof("LOG_WATCHPOINTS") - 1) == 0) {
2048061da546Spatrick           p += sizeof("LOG_WATCHPOINTS") - 1;
2049061da546Spatrick           bitmask |= LOG_WATCHPOINTS;
2050061da546Spatrick         } else if (strncmp(p, "LOG_STEP", sizeof("LOG_STEP") - 1) == 0) {
2051061da546Spatrick           p += sizeof("LOG_STEP") - 1;
2052061da546Spatrick           bitmask |= LOG_STEP;
2053061da546Spatrick         } else if (strncmp(p, "LOG_TASK", sizeof("LOG_TASK") - 1) == 0) {
2054061da546Spatrick           p += sizeof("LOG_TASK") - 1;
2055061da546Spatrick           bitmask |= LOG_TASK;
2056061da546Spatrick         } else if (strncmp(p, "LOG_ALL", sizeof("LOG_ALL") - 1) == 0) {
2057061da546Spatrick           p += sizeof("LOG_ALL") - 1;
2058061da546Spatrick           bitmask |= LOG_ALL;
2059061da546Spatrick         } else if (strncmp(p, "LOG_DEFAULT", sizeof("LOG_DEFAULT") - 1) == 0) {
2060061da546Spatrick           p += sizeof("LOG_DEFAULT") - 1;
2061061da546Spatrick           bitmask |= LOG_DEFAULT;
2062061da546Spatrick         }
2063061da546Spatrick         // end of auto-generated entries
2064061da546Spatrick 
2065061da546Spatrick         else if (strncmp(p, "LOG_NONE", sizeof("LOG_NONE") - 1) == 0) {
2066061da546Spatrick           p += sizeof("LOG_NONE") - 1;
2067061da546Spatrick           bitmask = 0;
2068061da546Spatrick         } else if (strncmp(p, "LOG_RNB_MINIMAL",
2069061da546Spatrick                            sizeof("LOG_RNB_MINIMAL") - 1) == 0) {
2070061da546Spatrick           p += sizeof("LOG_RNB_MINIMAL") - 1;
2071061da546Spatrick           bitmask |= LOG_RNB_MINIMAL;
2072061da546Spatrick         } else if (strncmp(p, "LOG_RNB_MEDIUM", sizeof("LOG_RNB_MEDIUM") - 1) ==
2073061da546Spatrick                    0) {
2074061da546Spatrick           p += sizeof("LOG_RNB_MEDIUM") - 1;
2075061da546Spatrick           bitmask |= LOG_RNB_MEDIUM;
2076061da546Spatrick         } else if (strncmp(p, "LOG_RNB_MAX", sizeof("LOG_RNB_MAX") - 1) == 0) {
2077061da546Spatrick           p += sizeof("LOG_RNB_MAX") - 1;
2078061da546Spatrick           bitmask |= LOG_RNB_MAX;
2079061da546Spatrick         } else if (strncmp(p, "LOG_RNB_COMM", sizeof("LOG_RNB_COMM") - 1) ==
2080061da546Spatrick                    0) {
2081061da546Spatrick           p += sizeof("LOG_RNB_COMM") - 1;
2082061da546Spatrick           bitmask |= LOG_RNB_COMM;
2083061da546Spatrick         } else if (strncmp(p, "LOG_RNB_REMOTE", sizeof("LOG_RNB_REMOTE") - 1) ==
2084061da546Spatrick                    0) {
2085061da546Spatrick           p += sizeof("LOG_RNB_REMOTE") - 1;
2086061da546Spatrick           bitmask |= LOG_RNB_REMOTE;
2087061da546Spatrick         } else if (strncmp(p, "LOG_RNB_EVENTS", sizeof("LOG_RNB_EVENTS") - 1) ==
2088061da546Spatrick                    0) {
2089061da546Spatrick           p += sizeof("LOG_RNB_EVENTS") - 1;
2090061da546Spatrick           bitmask |= LOG_RNB_EVENTS;
2091061da546Spatrick         } else if (strncmp(p, "LOG_RNB_PROC", sizeof("LOG_RNB_PROC") - 1) ==
2092061da546Spatrick                    0) {
2093061da546Spatrick           p += sizeof("LOG_RNB_PROC") - 1;
2094061da546Spatrick           bitmask |= LOG_RNB_PROC;
2095061da546Spatrick         } else if (strncmp(p, "LOG_RNB_PACKETS",
2096061da546Spatrick                            sizeof("LOG_RNB_PACKETS") - 1) == 0) {
2097061da546Spatrick           p += sizeof("LOG_RNB_PACKETS") - 1;
2098061da546Spatrick           bitmask |= LOG_RNB_PACKETS;
2099061da546Spatrick         } else if (strncmp(p, "LOG_RNB_ALL", sizeof("LOG_RNB_ALL") - 1) == 0) {
2100061da546Spatrick           p += sizeof("LOG_RNB_ALL") - 1;
2101061da546Spatrick           bitmask |= LOG_RNB_ALL;
2102061da546Spatrick         } else if (strncmp(p, "LOG_RNB_DEFAULT",
2103061da546Spatrick                            sizeof("LOG_RNB_DEFAULT") - 1) == 0) {
2104061da546Spatrick           p += sizeof("LOG_RNB_DEFAULT") - 1;
2105061da546Spatrick           bitmask |= LOG_RNB_DEFAULT;
2106061da546Spatrick         } else if (strncmp(p, "LOG_DARWIN_LOG", sizeof("LOG_DARWIN_LOG") - 1) ==
2107061da546Spatrick                    0) {
2108061da546Spatrick           p += sizeof("LOG_DARWIN_LOG") - 1;
2109061da546Spatrick           bitmask |= LOG_DARWIN_LOG;
2110061da546Spatrick         } else if (strncmp(p, "LOG_RNB_NONE", sizeof("LOG_RNB_NONE") - 1) ==
2111061da546Spatrick                    0) {
2112061da546Spatrick           p += sizeof("LOG_RNB_NONE") - 1;
2113061da546Spatrick           bitmask = 0;
2114061da546Spatrick         } else {
2115061da546Spatrick           /* Unrecognized logging bit; ignore it.  */
2116061da546Spatrick           const char *c = strchr(p, '|');
2117061da546Spatrick           if (c) {
2118061da546Spatrick             p = c;
2119061da546Spatrick           } else {
2120061da546Spatrick             c = strchr(p, ';');
2121061da546Spatrick             if (c) {
2122061da546Spatrick               p = c;
2123061da546Spatrick             } else {
2124061da546Spatrick               // Improperly terminated word; just go to end of str
2125061da546Spatrick               p = strchr(p, '\0');
2126061da546Spatrick             }
2127061da546Spatrick           }
2128061da546Spatrick         }
2129061da546Spatrick       }
2130061da546Spatrick       // Did we get a properly formatted logging bitmask?
2131061da546Spatrick       if (p && *p == ';') {
2132061da546Spatrick         // Enable DNB logging.
2133061da546Spatrick         // Use the existing log callback if one was already configured.
2134061da546Spatrick         if (!DNBLogGetLogCallback()) {
2135061da546Spatrick           // Use the os_log()-based logger if available; otherwise,
2136061da546Spatrick           // fallback to ASL.
2137061da546Spatrick           auto log_callback = OsLogger::GetLogFunction();
2138061da546Spatrick           if (log_callback)
2139061da546Spatrick             DNBLogSetLogCallback(log_callback, nullptr);
2140061da546Spatrick           else
2141061da546Spatrick             DNBLogSetLogCallback(ASLLogCallback, nullptr);
2142061da546Spatrick         }
2143061da546Spatrick 
2144061da546Spatrick         // Update logging to use the configured log channel bitmask.
2145061da546Spatrick         DNBLogSetLogMask(bitmask);
2146061da546Spatrick         p++;
2147061da546Spatrick       }
2148061da546Spatrick     }
2149061da546Spatrick // We're not going to support logging to a file for now.  All logging
2150061da546Spatrick // goes through ASL or the previously arranged log callback.
2151061da546Spatrick #if 0
2152061da546Spatrick         else if (strncmp (p, "mode=", sizeof ("mode=") - 1) == 0)
2153061da546Spatrick         {
2154061da546Spatrick             p += sizeof ("mode=") - 1;
2155061da546Spatrick             if (strncmp (p, "asl;", sizeof ("asl;") - 1) == 0)
2156061da546Spatrick             {
2157061da546Spatrick                 DNBLogToASL ();
2158061da546Spatrick                 p += sizeof ("asl;") - 1;
2159061da546Spatrick             }
2160061da546Spatrick             else if (strncmp (p, "file;", sizeof ("file;") - 1) == 0)
2161061da546Spatrick             {
2162061da546Spatrick                 DNBLogToFile ();
2163061da546Spatrick                 p += sizeof ("file;") - 1;
2164061da546Spatrick             }
2165061da546Spatrick             else
2166061da546Spatrick             {
2167061da546Spatrick                 // Ignore unknown argument
2168061da546Spatrick                 const char *c = strchr (p, ';');
2169061da546Spatrick                 if (c)
2170061da546Spatrick                     p = c + 1;
2171061da546Spatrick                 else
2172061da546Spatrick                     p = strchr (p, '\0');
2173061da546Spatrick             }
2174061da546Spatrick         }
2175061da546Spatrick         else if (strncmp (p, "filename=", sizeof ("filename=") - 1) == 0)
2176061da546Spatrick         {
2177061da546Spatrick             p += sizeof ("filename=") - 1;
2178061da546Spatrick             const char *c = strchr (p, ';');
2179061da546Spatrick             if (c == NULL)
2180061da546Spatrick             {
2181061da546Spatrick                 c = strchr (p, '\0');
2182061da546Spatrick                 continue;
2183061da546Spatrick             }
2184061da546Spatrick             char *fn = (char *) alloca (c - p + 1);
2185061da546Spatrick             strlcpy (fn, p, c - p);
2186061da546Spatrick             fn[c - p] = '\0';
2187061da546Spatrick 
2188061da546Spatrick             // A file name of "asl" is special and is another way to indicate
2189061da546Spatrick             // that logging should be done via ASL, not by file.
2190061da546Spatrick             if (strcmp (fn, "asl") == 0)
2191061da546Spatrick             {
2192061da546Spatrick                 DNBLogToASL ();
2193061da546Spatrick             }
2194061da546Spatrick             else
2195061da546Spatrick             {
2196061da546Spatrick                 FILE *f = fopen (fn, "w");
2197061da546Spatrick                 if (f)
2198061da546Spatrick                 {
2199061da546Spatrick                     DNBLogSetLogFile (f);
2200061da546Spatrick                     DNBEnableLogging (f, DNBLogGetLogMask ());
2201061da546Spatrick                     DNBLogToFile ();
2202061da546Spatrick                 }
2203061da546Spatrick             }
2204061da546Spatrick             p = c + 1;
2205061da546Spatrick         }
2206061da546Spatrick #endif /* #if 0 to enforce ASL logging only.  */
2207061da546Spatrick     else {
2208061da546Spatrick       // Ignore unknown argument
2209061da546Spatrick       const char *c = strchr(p, ';');
2210061da546Spatrick       if (c)
2211061da546Spatrick         p = c + 1;
2212061da546Spatrick       else
2213061da546Spatrick         p = strchr(p, '\0');
2214061da546Spatrick     }
2215061da546Spatrick   }
2216061da546Spatrick 
2217061da546Spatrick   return rnb_success;
2218061da546Spatrick }
2219061da546Spatrick 
HandlePacket_QSetIgnoredExceptions(const char * p)2220*f6aab3d8Srobert rnb_err_t RNBRemote::HandlePacket_QSetIgnoredExceptions(const char *p) {
2221*f6aab3d8Srobert   // We can't set the ignored exceptions if we have a running process:
2222*f6aab3d8Srobert   if (m_ctx.HasValidProcessID())
2223*f6aab3d8Srobert     return SendPacket("E35");
2224*f6aab3d8Srobert 
2225*f6aab3d8Srobert   p += sizeof("QSetIgnoredExceptions:") - 1;
2226*f6aab3d8Srobert   bool success = true;
2227*f6aab3d8Srobert   while(1) {
2228*f6aab3d8Srobert     const char *bar  = strchr(p, '|');
2229*f6aab3d8Srobert     if (bar == nullptr) {
2230*f6aab3d8Srobert       success = m_ctx.AddIgnoredException(p);
2231*f6aab3d8Srobert       break;
2232*f6aab3d8Srobert     } else {
2233*f6aab3d8Srobert       std::string exc_str(p, bar - p);
2234*f6aab3d8Srobert       if (exc_str.empty()) {
2235*f6aab3d8Srobert         success = false;
2236*f6aab3d8Srobert         break;
2237*f6aab3d8Srobert       }
2238*f6aab3d8Srobert 
2239*f6aab3d8Srobert       success = m_ctx.AddIgnoredException(exc_str.c_str());
2240*f6aab3d8Srobert       if (!success)
2241*f6aab3d8Srobert         break;
2242*f6aab3d8Srobert       p = bar + 1;
2243*f6aab3d8Srobert     }
2244*f6aab3d8Srobert   }
2245*f6aab3d8Srobert   if (success)
2246*f6aab3d8Srobert     return SendPacket("OK");
2247*f6aab3d8Srobert   else
2248*f6aab3d8Srobert     return SendPacket("E36");
2249*f6aab3d8Srobert }
2250*f6aab3d8Srobert 
HandlePacket_QThreadSuffixSupported(const char * p)2251061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QThreadSuffixSupported(const char *p) {
2252061da546Spatrick   m_thread_suffix_supported = true;
2253061da546Spatrick   return SendPacket("OK");
2254061da546Spatrick }
2255061da546Spatrick 
HandlePacket_QStartNoAckMode(const char * p)2256061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QStartNoAckMode(const char *p) {
2257061da546Spatrick   // Send the OK packet first so the correct checksum is appended...
2258061da546Spatrick   rnb_err_t result = SendPacket("OK");
2259061da546Spatrick   m_noack_mode = true;
2260061da546Spatrick   return result;
2261061da546Spatrick }
2262061da546Spatrick 
HandlePacket_QSetLogging(const char * p)2263061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetLogging(const char *p) {
2264061da546Spatrick   p += sizeof("QSetLogging:") - 1;
2265061da546Spatrick   rnb_err_t result = set_logging(p);
2266061da546Spatrick   if (result == rnb_success)
2267061da546Spatrick     return SendPacket("OK");
2268061da546Spatrick   else
2269061da546Spatrick     return SendPacket("E35");
2270061da546Spatrick }
2271061da546Spatrick 
HandlePacket_QSetDisableASLR(const char * p)2272061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetDisableASLR(const char *p) {
2273061da546Spatrick   extern int g_disable_aslr;
2274061da546Spatrick   p += sizeof("QSetDisableASLR:") - 1;
2275061da546Spatrick   switch (*p) {
2276061da546Spatrick   case '0':
2277061da546Spatrick     g_disable_aslr = 0;
2278061da546Spatrick     break;
2279061da546Spatrick   case '1':
2280061da546Spatrick     g_disable_aslr = 1;
2281061da546Spatrick     break;
2282061da546Spatrick   default:
2283061da546Spatrick     return SendPacket("E56");
2284061da546Spatrick   }
2285061da546Spatrick   return SendPacket("OK");
2286061da546Spatrick }
2287061da546Spatrick 
HandlePacket_QSetSTDIO(const char * p)2288061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetSTDIO(const char *p) {
2289061da546Spatrick   // Only set stdin/out/err if we don't already have a process
2290061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
2291061da546Spatrick     bool success = false;
2292061da546Spatrick     // Check the seventh character since the packet will be one of:
2293061da546Spatrick     // QSetSTDIN
2294061da546Spatrick     // QSetSTDOUT
2295061da546Spatrick     // QSetSTDERR
2296061da546Spatrick     StdStringExtractor packet(p);
2297061da546Spatrick     packet.SetFilePos(7);
2298061da546Spatrick     char ch = packet.GetChar();
2299061da546Spatrick     while (packet.GetChar() != ':')
2300061da546Spatrick       /* Do nothing. */;
2301061da546Spatrick 
2302061da546Spatrick     switch (ch) {
2303061da546Spatrick     case 'I': // STDIN
2304061da546Spatrick       packet.GetHexByteString(m_ctx.GetSTDIN());
2305061da546Spatrick       success = !m_ctx.GetSTDIN().empty();
2306061da546Spatrick       break;
2307061da546Spatrick 
2308061da546Spatrick     case 'O': // STDOUT
2309061da546Spatrick       packet.GetHexByteString(m_ctx.GetSTDOUT());
2310061da546Spatrick       success = !m_ctx.GetSTDOUT().empty();
2311061da546Spatrick       break;
2312061da546Spatrick 
2313061da546Spatrick     case 'E': // STDERR
2314061da546Spatrick       packet.GetHexByteString(m_ctx.GetSTDERR());
2315061da546Spatrick       success = !m_ctx.GetSTDERR().empty();
2316061da546Spatrick       break;
2317061da546Spatrick 
2318061da546Spatrick     default:
2319061da546Spatrick       break;
2320061da546Spatrick     }
2321061da546Spatrick     if (success)
2322061da546Spatrick       return SendPacket("OK");
2323061da546Spatrick     return SendPacket("E57");
2324061da546Spatrick   }
2325061da546Spatrick   return SendPacket("E58");
2326061da546Spatrick }
2327061da546Spatrick 
HandlePacket_QSetWorkingDir(const char * p)2328061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetWorkingDir(const char *p) {
2329061da546Spatrick   // Only set the working directory if we don't already have a process
2330061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
2331061da546Spatrick     StdStringExtractor packet(p += sizeof("QSetWorkingDir:") - 1);
2332061da546Spatrick     if (packet.GetHexByteString(m_ctx.GetWorkingDir())) {
2333061da546Spatrick       struct stat working_dir_stat;
2334061da546Spatrick       if (::stat(m_ctx.GetWorkingDirPath(), &working_dir_stat) == -1) {
2335061da546Spatrick         m_ctx.GetWorkingDir().clear();
2336061da546Spatrick         return SendPacket("E61"); // Working directory doesn't exist...
2337061da546Spatrick       } else if ((working_dir_stat.st_mode & S_IFMT) == S_IFDIR) {
2338061da546Spatrick         return SendPacket("OK");
2339061da546Spatrick       } else {
2340061da546Spatrick         m_ctx.GetWorkingDir().clear();
2341061da546Spatrick         return SendPacket("E62"); // Working directory isn't a directory...
2342061da546Spatrick       }
2343061da546Spatrick     }
2344061da546Spatrick     return SendPacket("E59"); // Invalid path
2345061da546Spatrick   }
2346061da546Spatrick   return SendPacket(
2347061da546Spatrick       "E60"); // Already had a process, too late to set working dir
2348061da546Spatrick }
2349061da546Spatrick 
HandlePacket_QSyncThreadState(const char * p)2350061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSyncThreadState(const char *p) {
2351061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
2352061da546Spatrick     // We allow gdb to connect to a server that hasn't started running
2353061da546Spatrick     // the target yet.  gdb still wants to ask questions about it and
2354061da546Spatrick     // freaks out if it gets an error.  So just return OK here.
2355061da546Spatrick     return SendPacket("OK");
2356061da546Spatrick   }
2357061da546Spatrick 
2358061da546Spatrick   errno = 0;
2359061da546Spatrick   p += strlen("QSyncThreadState:");
2360061da546Spatrick   nub_thread_t tid = strtoul(p, NULL, 16);
2361061da546Spatrick   if (errno != 0 && tid == 0) {
2362061da546Spatrick     return HandlePacket_ILLFORMED(
2363061da546Spatrick         __FILE__, __LINE__, p,
2364061da546Spatrick         "Invalid thread number in QSyncThreadState packet");
2365061da546Spatrick   }
2366061da546Spatrick   if (DNBProcessSyncThreadState(m_ctx.ProcessID(), tid))
2367061da546Spatrick     return SendPacket("OK");
2368061da546Spatrick   else
2369061da546Spatrick     return SendPacket("E61");
2370061da546Spatrick }
2371061da546Spatrick 
HandlePacket_QSetDetachOnError(const char * p)2372061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetDetachOnError(const char *p) {
2373061da546Spatrick   p += sizeof("QSetDetachOnError:") - 1;
2374061da546Spatrick   bool should_detach = true;
2375061da546Spatrick   switch (*p) {
2376061da546Spatrick   case '0':
2377061da546Spatrick     should_detach = false;
2378061da546Spatrick     break;
2379061da546Spatrick   case '1':
2380061da546Spatrick     should_detach = true;
2381061da546Spatrick     break;
2382061da546Spatrick   default:
2383061da546Spatrick     return HandlePacket_ILLFORMED(
2384061da546Spatrick         __FILE__, __LINE__, p,
2385061da546Spatrick         "Invalid value for QSetDetachOnError - should be 0 or 1");
2386061da546Spatrick     break;
2387061da546Spatrick   }
2388061da546Spatrick 
2389061da546Spatrick   m_ctx.SetDetachOnError(should_detach);
2390061da546Spatrick   return SendPacket("OK");
2391061da546Spatrick }
2392061da546Spatrick 
HandlePacket_QListThreadsInStopReply(const char * p)2393061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QListThreadsInStopReply(const char *p) {
2394061da546Spatrick   // If this packet is received, it allows us to send an extra key/value
2395061da546Spatrick   // pair in the stop reply packets where we will list all of the thread IDs
2396061da546Spatrick   // separated by commas:
2397061da546Spatrick   //
2398061da546Spatrick   //  "threads:10a,10b,10c;"
2399061da546Spatrick   //
2400061da546Spatrick   // This will get included in the stop reply packet as something like:
2401061da546Spatrick   //
2402061da546Spatrick   //  "T11thread:10a;00:00000000;01:00010203:threads:10a,10b,10c;"
2403061da546Spatrick   //
2404061da546Spatrick   // This can save two packets on each stop: qfThreadInfo/qsThreadInfo and
2405061da546Spatrick   // speed things up a bit.
2406061da546Spatrick   //
2407061da546Spatrick   // Send the OK packet first so the correct checksum is appended...
2408061da546Spatrick   rnb_err_t result = SendPacket("OK");
2409061da546Spatrick   m_list_threads_in_stop_reply = true;
2410061da546Spatrick 
2411061da546Spatrick   return result;
2412061da546Spatrick }
2413061da546Spatrick 
HandlePacket_QSetMaxPayloadSize(const char * p)2414061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetMaxPayloadSize(const char *p) {
2415061da546Spatrick   /* The number of characters in a packet payload that gdb is
2416061da546Spatrick    prepared to accept.  The packet-start char, packet-end char,
2417061da546Spatrick    2 checksum chars and terminating null character are not included
2418061da546Spatrick    in this size.  */
2419061da546Spatrick   p += sizeof("QSetMaxPayloadSize:") - 1;
2420061da546Spatrick   errno = 0;
2421061da546Spatrick   uint32_t size = static_cast<uint32_t>(strtoul(p, NULL, 16));
2422061da546Spatrick   if (errno != 0 && size == 0) {
2423061da546Spatrick     return HandlePacket_ILLFORMED(
2424061da546Spatrick         __FILE__, __LINE__, p, "Invalid length in QSetMaxPayloadSize packet");
2425061da546Spatrick   }
2426061da546Spatrick   m_max_payload_size = size;
2427061da546Spatrick   return SendPacket("OK");
2428061da546Spatrick }
2429061da546Spatrick 
HandlePacket_QSetMaxPacketSize(const char * p)2430061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetMaxPacketSize(const char *p) {
2431061da546Spatrick   /* This tells us the largest packet that gdb can handle.
2432061da546Spatrick    i.e. the size of gdb's packet-reading buffer.
2433061da546Spatrick    QSetMaxPayloadSize is preferred because it is less ambiguous.  */
2434061da546Spatrick   p += sizeof("QSetMaxPacketSize:") - 1;
2435061da546Spatrick   errno = 0;
2436061da546Spatrick   uint32_t size = static_cast<uint32_t>(strtoul(p, NULL, 16));
2437061da546Spatrick   if (errno != 0 && size == 0) {
2438061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
2439061da546Spatrick                                   "Invalid length in QSetMaxPacketSize packet");
2440061da546Spatrick   }
2441061da546Spatrick   m_max_payload_size = size - 5;
2442061da546Spatrick   return SendPacket("OK");
2443061da546Spatrick }
2444061da546Spatrick 
HandlePacket_QEnvironment(const char * p)2445061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QEnvironment(const char *p) {
2446061da546Spatrick   /* This sets the environment for the target program.  The packet is of the
2447061da546Spatrick    form:
2448061da546Spatrick 
2449061da546Spatrick    QEnvironment:VARIABLE=VALUE
2450061da546Spatrick 
2451061da546Spatrick    */
2452061da546Spatrick 
2453061da546Spatrick   DNBLogThreadedIf(
2454061da546Spatrick       LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironment: \"%s\"",
2455061da546Spatrick       (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p);
2456061da546Spatrick 
2457061da546Spatrick   p += sizeof("QEnvironment:") - 1;
2458061da546Spatrick   RNBContext &ctx = Context();
2459061da546Spatrick 
2460061da546Spatrick   ctx.PushEnvironment(p);
2461061da546Spatrick   return SendPacket("OK");
2462061da546Spatrick }
2463061da546Spatrick 
HandlePacket_QEnvironmentHexEncoded(const char * p)2464061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QEnvironmentHexEncoded(const char *p) {
2465061da546Spatrick   /* This sets the environment for the target program.  The packet is of the
2466061da546Spatrick      form:
2467061da546Spatrick 
2468061da546Spatrick       QEnvironmentHexEncoded:VARIABLE=VALUE
2469061da546Spatrick 
2470061da546Spatrick       The VARIABLE=VALUE part is sent hex-encoded so characters like '#' with
2471061da546Spatrick      special
2472061da546Spatrick       meaning in the remote protocol won't break it.
2473061da546Spatrick   */
2474061da546Spatrick 
2475061da546Spatrick   DNBLogThreadedIf(LOG_RNB_REMOTE,
2476061da546Spatrick                    "%8u RNBRemote::%s Handling QEnvironmentHexEncoded: \"%s\"",
2477061da546Spatrick                    (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
2478061da546Spatrick                    __FUNCTION__, p);
2479061da546Spatrick 
2480061da546Spatrick   p += sizeof("QEnvironmentHexEncoded:") - 1;
2481061da546Spatrick 
2482061da546Spatrick   std::string arg;
2483061da546Spatrick   const char *c;
2484061da546Spatrick   c = p;
2485061da546Spatrick   while (*c != '\0') {
2486061da546Spatrick     if (*(c + 1) == '\0') {
2487061da546Spatrick       return HandlePacket_ILLFORMED(
2488061da546Spatrick           __FILE__, __LINE__, p,
2489061da546Spatrick           "non-hex char in arg on 'QEnvironmentHexEncoded' pkt");
2490061da546Spatrick     }
2491061da546Spatrick     char smallbuf[3];
2492061da546Spatrick     smallbuf[0] = *c;
2493061da546Spatrick     smallbuf[1] = *(c + 1);
2494061da546Spatrick     smallbuf[2] = '\0';
2495061da546Spatrick     errno = 0;
2496061da546Spatrick     int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
2497061da546Spatrick     if (errno != 0 && ch == 0) {
2498061da546Spatrick       return HandlePacket_ILLFORMED(
2499061da546Spatrick           __FILE__, __LINE__, p,
2500061da546Spatrick           "non-hex char in arg on 'QEnvironmentHexEncoded' pkt");
2501061da546Spatrick     }
2502061da546Spatrick     arg.push_back(ch);
2503061da546Spatrick     c += 2;
2504061da546Spatrick   }
2505061da546Spatrick 
2506061da546Spatrick   RNBContext &ctx = Context();
2507061da546Spatrick   if (arg.length() > 0)
2508061da546Spatrick     ctx.PushEnvironment(arg.c_str());
2509061da546Spatrick 
2510061da546Spatrick   return SendPacket("OK");
2511061da546Spatrick }
2512061da546Spatrick 
HandlePacket_QLaunchArch(const char * p)2513061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QLaunchArch(const char *p) {
2514061da546Spatrick   p += sizeof("QLaunchArch:") - 1;
2515061da546Spatrick   if (DNBSetArchitecture(p))
2516061da546Spatrick     return SendPacket("OK");
2517061da546Spatrick   return SendPacket("E63");
2518061da546Spatrick }
2519061da546Spatrick 
HandlePacket_QSetProcessEvent(const char * p)2520061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QSetProcessEvent(const char *p) {
2521061da546Spatrick   p += sizeof("QSetProcessEvent:") - 1;
2522061da546Spatrick   // If the process is running, then send the event to the process, otherwise
2523061da546Spatrick   // store it in the context.
2524061da546Spatrick   if (Context().HasValidProcessID()) {
2525061da546Spatrick     if (DNBProcessSendEvent(Context().ProcessID(), p))
2526061da546Spatrick       return SendPacket("OK");
2527061da546Spatrick     else
2528061da546Spatrick       return SendPacket("E80");
2529061da546Spatrick   } else {
2530061da546Spatrick     Context().PushProcessEvent(p);
2531061da546Spatrick   }
2532061da546Spatrick   return SendPacket("OK");
2533061da546Spatrick }
2534061da546Spatrick 
append_hex_value(std::ostream & ostrm,const void * buf,size_t buf_size,bool swap)2535061da546Spatrick void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size,
2536061da546Spatrick                       bool swap) {
2537061da546Spatrick   int i;
2538061da546Spatrick   const uint8_t *p = (const uint8_t *)buf;
2539061da546Spatrick   if (swap) {
2540061da546Spatrick     for (i = static_cast<int>(buf_size) - 1; i >= 0; i--)
2541061da546Spatrick       ostrm << RAWHEX8(p[i]);
2542061da546Spatrick   } else {
2543061da546Spatrick     for (size_t i = 0; i < buf_size; i++)
2544061da546Spatrick       ostrm << RAWHEX8(p[i]);
2545061da546Spatrick   }
2546061da546Spatrick }
2547061da546Spatrick 
cstring_to_asciihex_string(const char * str)2548061da546Spatrick std::string cstring_to_asciihex_string(const char *str) {
2549061da546Spatrick   std::string hex_str;
2550061da546Spatrick   hex_str.reserve (strlen (str) * 2);
2551061da546Spatrick   while (str && *str) {
2552dda28197Spatrick     uint8_t c = *str++;
2553061da546Spatrick     char hexbuf[5];
2554dda28197Spatrick     snprintf (hexbuf, sizeof(hexbuf), "%02x", c);
2555061da546Spatrick     hex_str += hexbuf;
2556061da546Spatrick   }
2557061da546Spatrick   return hex_str;
2558061da546Spatrick }
2559061da546Spatrick 
append_hexified_string(std::ostream & ostrm,const std::string & string)2560061da546Spatrick void append_hexified_string(std::ostream &ostrm, const std::string &string) {
2561061da546Spatrick   size_t string_size = string.size();
2562061da546Spatrick   const char *string_buf = string.c_str();
2563061da546Spatrick   for (size_t i = 0; i < string_size; i++) {
2564061da546Spatrick     ostrm << RAWHEX8(*(string_buf + i));
2565061da546Spatrick   }
2566061da546Spatrick }
2567061da546Spatrick 
register_value_in_hex_fixed_width(std::ostream & ostrm,nub_process_t pid,nub_thread_t tid,const register_map_entry_t * reg,const DNBRegisterValue * reg_value_ptr)2568061da546Spatrick void register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid,
2569061da546Spatrick                                        nub_thread_t tid,
2570061da546Spatrick                                        const register_map_entry_t *reg,
2571061da546Spatrick                                        const DNBRegisterValue *reg_value_ptr) {
2572061da546Spatrick   if (reg != NULL) {
2573061da546Spatrick     DNBRegisterValue reg_value;
2574061da546Spatrick     if (reg_value_ptr == NULL) {
2575061da546Spatrick       if (DNBThreadGetRegisterValueByID(pid, tid, reg->nub_info.set,
2576061da546Spatrick                                         reg->nub_info.reg, &reg_value))
2577061da546Spatrick         reg_value_ptr = &reg_value;
2578061da546Spatrick     }
2579061da546Spatrick 
2580061da546Spatrick     if (reg_value_ptr) {
2581061da546Spatrick       append_hex_value(ostrm, reg_value_ptr->value.v_uint8, reg->nub_info.size,
2582061da546Spatrick                        false);
2583061da546Spatrick     } else {
2584061da546Spatrick       // If we fail to read a register value, check if it has a default
2585061da546Spatrick       // fail value. If it does, return this instead in case some of
2586061da546Spatrick       // the registers are not available on the current system.
2587061da546Spatrick       if (reg->nub_info.size > 0) {
2588061da546Spatrick         std::basic_string<uint8_t> zeros(reg->nub_info.size, '\0');
2589061da546Spatrick         append_hex_value(ostrm, zeros.data(), zeros.size(), false);
2590061da546Spatrick       }
2591061da546Spatrick     }
2592061da546Spatrick   }
2593061da546Spatrick }
2594061da546Spatrick 
debugserver_regnum_with_fixed_width_hex_register_value(std::ostream & ostrm,nub_process_t pid,nub_thread_t tid,const register_map_entry_t * reg,const DNBRegisterValue * reg_value_ptr)2595061da546Spatrick void debugserver_regnum_with_fixed_width_hex_register_value(
2596061da546Spatrick     std::ostream &ostrm, nub_process_t pid, nub_thread_t tid,
2597061da546Spatrick     const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr) {
2598061da546Spatrick   // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX
2599061da546Spatrick   // gdb register number, and VVVVVVVV is the correct number of hex bytes
2600061da546Spatrick   // as ASCII for the register value.
2601061da546Spatrick   if (reg != NULL) {
2602061da546Spatrick     ostrm << RAWHEX8(reg->debugserver_regnum) << ':';
2603061da546Spatrick     register_value_in_hex_fixed_width(ostrm, pid, tid, reg, reg_value_ptr);
2604061da546Spatrick     ostrm << ';';
2605061da546Spatrick   }
2606061da546Spatrick }
2607061da546Spatrick 
GetThreadQueueInfo(nub_process_t pid,nub_addr_t dispatch_qaddr,nub_addr_t & dispatch_queue_t,std::string & queue_name,uint64_t & queue_width,uint64_t & queue_serialnum) const2608061da546Spatrick void RNBRemote::DispatchQueueOffsets::GetThreadQueueInfo(
2609061da546Spatrick     nub_process_t pid, nub_addr_t dispatch_qaddr, nub_addr_t &dispatch_queue_t,
2610061da546Spatrick     std::string &queue_name, uint64_t &queue_width,
2611061da546Spatrick     uint64_t &queue_serialnum) const {
2612061da546Spatrick   queue_name.clear();
2613061da546Spatrick   queue_width = 0;
2614061da546Spatrick   queue_serialnum = 0;
2615061da546Spatrick 
2616061da546Spatrick   if (IsValid() && dispatch_qaddr != INVALID_NUB_ADDRESS &&
2617061da546Spatrick       dispatch_qaddr != 0) {
2618061da546Spatrick     dispatch_queue_t = DNBProcessMemoryReadPointer(pid, dispatch_qaddr);
2619061da546Spatrick     if (dispatch_queue_t) {
2620061da546Spatrick       queue_width = DNBProcessMemoryReadInteger(
2621061da546Spatrick           pid, dispatch_queue_t + dqo_width, dqo_width_size, 0);
2622061da546Spatrick       queue_serialnum = DNBProcessMemoryReadInteger(
2623061da546Spatrick           pid, dispatch_queue_t + dqo_serialnum, dqo_serialnum_size, 0);
2624061da546Spatrick 
2625061da546Spatrick       if (dqo_version >= 4) {
2626061da546Spatrick         // libdispatch versions 4+, pointer to dispatch name is in the
2627061da546Spatrick         // queue structure.
2628061da546Spatrick         nub_addr_t pointer_to_label_address = dispatch_queue_t + dqo_label;
2629061da546Spatrick         nub_addr_t label_addr =
2630061da546Spatrick             DNBProcessMemoryReadPointer(pid, pointer_to_label_address);
2631061da546Spatrick         if (label_addr)
2632061da546Spatrick           queue_name = DNBProcessMemoryReadCString(pid, label_addr);
2633061da546Spatrick       } else {
2634061da546Spatrick         // libdispatch versions 1-3, dispatch name is a fixed width char array
2635061da546Spatrick         // in the queue structure.
2636061da546Spatrick         queue_name = DNBProcessMemoryReadCStringFixed(
2637061da546Spatrick             pid, dispatch_queue_t + dqo_label, dqo_label_size);
2638061da546Spatrick       }
2639061da546Spatrick     }
2640061da546Spatrick   }
2641061da546Spatrick }
2642061da546Spatrick 
2643061da546Spatrick struct StackMemory {
2644061da546Spatrick   uint8_t bytes[2 * sizeof(nub_addr_t)];
2645061da546Spatrick   nub_size_t length;
2646061da546Spatrick };
2647061da546Spatrick typedef std::map<nub_addr_t, StackMemory> StackMemoryMap;
2648061da546Spatrick 
ReadStackMemory(nub_process_t pid,nub_thread_t tid,StackMemoryMap & stack_mmap,uint32_t backtrace_limit=256)2649061da546Spatrick static void ReadStackMemory(nub_process_t pid, nub_thread_t tid,
2650061da546Spatrick                             StackMemoryMap &stack_mmap,
2651061da546Spatrick                             uint32_t backtrace_limit = 256) {
2652061da546Spatrick   DNBRegisterValue reg_value;
2653061da546Spatrick   if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC,
2654061da546Spatrick                                     GENERIC_REGNUM_FP, &reg_value)) {
2655061da546Spatrick     uint32_t frame_count = 0;
2656061da546Spatrick     uint64_t fp = 0;
2657061da546Spatrick     if (reg_value.info.size == 4)
2658061da546Spatrick       fp = reg_value.value.uint32;
2659061da546Spatrick     else
2660061da546Spatrick       fp = reg_value.value.uint64;
2661061da546Spatrick     while (fp != 0) {
2662061da546Spatrick       // Make sure we never recurse more than 256 times so we don't recurse too
2663061da546Spatrick       // far or
2664061da546Spatrick       // store up too much memory in the expedited cache
2665061da546Spatrick       if (++frame_count > backtrace_limit)
2666061da546Spatrick         break;
2667061da546Spatrick 
2668061da546Spatrick       const nub_size_t read_size = reg_value.info.size * 2;
2669061da546Spatrick       StackMemory stack_memory;
2670061da546Spatrick       stack_memory.length = read_size;
2671061da546Spatrick       if (DNBProcessMemoryRead(pid, fp, read_size, stack_memory.bytes) !=
2672061da546Spatrick           read_size)
2673061da546Spatrick         break;
2674061da546Spatrick       // Make sure we don't try to put the same stack memory in more than once
2675061da546Spatrick       if (stack_mmap.find(fp) != stack_mmap.end())
2676061da546Spatrick         break;
2677061da546Spatrick       // Put the entry into the cache
2678061da546Spatrick       stack_mmap[fp] = stack_memory;
2679061da546Spatrick       // Dereference the frame pointer to get to the previous frame pointer
2680061da546Spatrick       if (reg_value.info.size == 4)
2681061da546Spatrick         fp = ((uint32_t *)stack_memory.bytes)[0];
2682061da546Spatrick       else
2683061da546Spatrick         fp = ((uint64_t *)stack_memory.bytes)[0];
2684061da546Spatrick     }
2685061da546Spatrick   }
2686061da546Spatrick }
2687061da546Spatrick 
SendStopReplyPacketForThread(nub_thread_t tid)2688061da546Spatrick rnb_err_t RNBRemote::SendStopReplyPacketForThread(nub_thread_t tid) {
2689061da546Spatrick   const nub_process_t pid = m_ctx.ProcessID();
2690061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
2691061da546Spatrick     return SendPacket("E50");
2692061da546Spatrick 
2693061da546Spatrick   struct DNBThreadStopInfo tid_stop_info;
2694061da546Spatrick 
2695061da546Spatrick   /* Fill the remaining space in this packet with as many registers
2696061da546Spatrick    as we can stuff in there.  */
2697061da546Spatrick 
2698061da546Spatrick   if (DNBThreadGetStopReason(pid, tid, &tid_stop_info)) {
2699061da546Spatrick     const bool did_exec = tid_stop_info.reason == eStopTypeExec;
2700061da546Spatrick     if (did_exec) {
2701061da546Spatrick       RNBRemote::InitializeRegisters(true);
2702061da546Spatrick 
2703061da546Spatrick       // Reset any symbols that need resetting when we exec
2704061da546Spatrick       m_dispatch_queue_offsets_addr = INVALID_NUB_ADDRESS;
2705061da546Spatrick       m_dispatch_queue_offsets.Clear();
2706061da546Spatrick     }
2707061da546Spatrick 
2708061da546Spatrick     std::ostringstream ostrm;
2709061da546Spatrick     // Output the T packet with the thread
2710061da546Spatrick     ostrm << 'T';
2711061da546Spatrick     int signum = tid_stop_info.details.signal.signo;
2712061da546Spatrick     DNBLogThreadedIf(
2713061da546Spatrick         LOG_RNB_PROC, "%8d %s got signal signo = %u, exc_type = %u",
2714061da546Spatrick         (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
2715061da546Spatrick         signum, tid_stop_info.details.exception.type);
2716061da546Spatrick 
2717061da546Spatrick     // Translate any mach exceptions to gdb versions, unless they are
2718061da546Spatrick     // common exceptions like a breakpoint or a soft signal.
2719061da546Spatrick     switch (tid_stop_info.details.exception.type) {
2720061da546Spatrick     default:
2721061da546Spatrick       signum = 0;
2722061da546Spatrick       break;
2723061da546Spatrick     case EXC_BREAKPOINT:
2724061da546Spatrick       signum = SIGTRAP;
2725061da546Spatrick       break;
2726061da546Spatrick     case EXC_BAD_ACCESS:
2727061da546Spatrick       signum = TARGET_EXC_BAD_ACCESS;
2728061da546Spatrick       break;
2729061da546Spatrick     case EXC_BAD_INSTRUCTION:
2730061da546Spatrick       signum = TARGET_EXC_BAD_INSTRUCTION;
2731061da546Spatrick       break;
2732061da546Spatrick     case EXC_ARITHMETIC:
2733061da546Spatrick       signum = TARGET_EXC_ARITHMETIC;
2734061da546Spatrick       break;
2735061da546Spatrick     case EXC_EMULATION:
2736061da546Spatrick       signum = TARGET_EXC_EMULATION;
2737061da546Spatrick       break;
2738061da546Spatrick     case EXC_SOFTWARE:
2739061da546Spatrick       if (tid_stop_info.details.exception.data_count == 2 &&
2740061da546Spatrick           tid_stop_info.details.exception.data[0] == EXC_SOFT_SIGNAL)
2741061da546Spatrick         signum = static_cast<int>(tid_stop_info.details.exception.data[1]);
2742061da546Spatrick       else
2743061da546Spatrick         signum = TARGET_EXC_SOFTWARE;
2744061da546Spatrick       break;
2745061da546Spatrick     }
2746061da546Spatrick 
2747061da546Spatrick     ostrm << RAWHEX8(signum & 0xff);
2748061da546Spatrick 
2749061da546Spatrick     ostrm << std::hex << "thread:" << tid << ';';
2750061da546Spatrick 
2751061da546Spatrick     const char *thread_name = DNBThreadGetName(pid, tid);
2752061da546Spatrick     if (thread_name && thread_name[0]) {
2753061da546Spatrick       size_t thread_name_len = strlen(thread_name);
2754061da546Spatrick 
2755061da546Spatrick       if (::strcspn(thread_name, "$#+-;:") == thread_name_len)
2756061da546Spatrick         ostrm << std::hex << "name:" << thread_name << ';';
2757061da546Spatrick       else {
2758061da546Spatrick         // the thread name contains special chars, send as hex bytes
2759061da546Spatrick         ostrm << std::hex << "hexname:";
2760061da546Spatrick         const uint8_t *u_thread_name = (const uint8_t *)thread_name;
2761061da546Spatrick         for (size_t i = 0; i < thread_name_len; i++)
2762061da546Spatrick           ostrm << RAWHEX8(u_thread_name[i]);
2763061da546Spatrick         ostrm << ';';
2764061da546Spatrick       }
2765061da546Spatrick     }
2766061da546Spatrick 
2767061da546Spatrick     // If a 'QListThreadsInStopReply' was sent to enable this feature, we
2768061da546Spatrick     // will send all thread IDs back in the "threads" key whose value is
2769061da546Spatrick     // a list of hex thread IDs separated by commas:
2770061da546Spatrick     //  "threads:10a,10b,10c;"
2771061da546Spatrick     // This will save the debugger from having to send a pair of qfThreadInfo
2772061da546Spatrick     // and qsThreadInfo packets, but it also might take a lot of room in the
2773061da546Spatrick     // stop reply packet, so it must be enabled only on systems where there
2774061da546Spatrick     // are no limits on packet lengths.
2775061da546Spatrick     if (m_list_threads_in_stop_reply) {
2776061da546Spatrick       const nub_size_t numthreads = DNBProcessGetNumThreads(pid);
2777061da546Spatrick       if (numthreads > 0) {
2778061da546Spatrick         std::vector<uint64_t> pc_values;
2779061da546Spatrick         ostrm << std::hex << "threads:";
2780061da546Spatrick         for (nub_size_t i = 0; i < numthreads; ++i) {
2781061da546Spatrick           nub_thread_t th = DNBProcessGetThreadAtIndex(pid, i);
2782061da546Spatrick           if (i > 0)
2783061da546Spatrick             ostrm << ',';
2784061da546Spatrick           ostrm << std::hex << th;
2785061da546Spatrick           DNBRegisterValue pc_regval;
2786061da546Spatrick           if (DNBThreadGetRegisterValueByID(pid, th, REGISTER_SET_GENERIC,
2787061da546Spatrick                                             GENERIC_REGNUM_PC, &pc_regval)) {
2788061da546Spatrick             uint64_t pc = INVALID_NUB_ADDRESS;
2789061da546Spatrick             if (pc_regval.value.uint64 != INVALID_NUB_ADDRESS) {
2790061da546Spatrick               if (pc_regval.info.size == 4) {
2791061da546Spatrick                 pc = pc_regval.value.uint32;
2792061da546Spatrick               } else if (pc_regval.info.size == 8) {
2793061da546Spatrick                 pc = pc_regval.value.uint64;
2794061da546Spatrick               }
2795061da546Spatrick               if (pc != INVALID_NUB_ADDRESS) {
2796061da546Spatrick                 pc_values.push_back(pc);
2797061da546Spatrick               }
2798061da546Spatrick             }
2799061da546Spatrick           }
2800061da546Spatrick         }
2801061da546Spatrick         ostrm << ';';
2802061da546Spatrick 
2803061da546Spatrick         // If we failed to get any of the thread pc values, the size of our
2804061da546Spatrick         // vector will not
2805061da546Spatrick         // be the same as the # of threads.  Don't provide any expedited thread
2806061da546Spatrick         // pc values in
2807061da546Spatrick         // that case.  This should not happen.
2808061da546Spatrick         if (pc_values.size() == numthreads) {
2809061da546Spatrick           ostrm << std::hex << "thread-pcs:";
2810061da546Spatrick           for (nub_size_t i = 0; i < numthreads; ++i) {
2811061da546Spatrick             if (i > 0)
2812061da546Spatrick               ostrm << ',';
2813061da546Spatrick             ostrm << std::hex << pc_values[i];
2814061da546Spatrick           }
2815061da546Spatrick           ostrm << ';';
2816061da546Spatrick         }
2817061da546Spatrick       }
2818061da546Spatrick 
2819061da546Spatrick       // Include JSON info that describes the stop reason for any threads
2820061da546Spatrick       // that actually have stop reasons. We use the new "jstopinfo" key
2821061da546Spatrick       // whose values is hex ascii JSON that contains the thread IDs
2822061da546Spatrick       // thread stop info only for threads that have stop reasons. Only send
2823061da546Spatrick       // this if we have more than one thread otherwise this packet has all
2824061da546Spatrick       // the info it needs.
2825061da546Spatrick       if (numthreads > 1) {
2826061da546Spatrick         const bool threads_with_valid_stop_info_only = true;
2827061da546Spatrick         JSONGenerator::ObjectSP threads_info_sp =
2828061da546Spatrick             GetJSONThreadsInfo(threads_with_valid_stop_info_only);
2829061da546Spatrick         if (threads_info_sp) {
2830061da546Spatrick           ostrm << std::hex << "jstopinfo:";
2831061da546Spatrick           std::ostringstream json_strm;
2832061da546Spatrick           threads_info_sp->Dump(json_strm);
2833*f6aab3d8Srobert           threads_info_sp->Clear();
2834061da546Spatrick           append_hexified_string(ostrm, json_strm.str());
2835061da546Spatrick           ostrm << ';';
2836061da546Spatrick         }
2837061da546Spatrick       }
2838061da546Spatrick     }
2839061da546Spatrick 
2840061da546Spatrick     if (g_num_reg_entries == 0)
2841061da546Spatrick       InitializeRegisters();
2842061da546Spatrick 
2843061da546Spatrick     if (g_reg_entries != NULL) {
2844061da546Spatrick       DNBRegisterValue reg_value;
2845061da546Spatrick       for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) {
2846061da546Spatrick         // Expedite all registers in the first register set that aren't
2847061da546Spatrick         // contained in other registers
2848061da546Spatrick         if (g_reg_entries[reg].nub_info.set == 1 &&
2849061da546Spatrick             g_reg_entries[reg].nub_info.value_regs == NULL) {
2850061da546Spatrick           if (!DNBThreadGetRegisterValueByID(
2851061da546Spatrick                   pid, tid, g_reg_entries[reg].nub_info.set,
2852061da546Spatrick                   g_reg_entries[reg].nub_info.reg, &reg_value))
2853061da546Spatrick             continue;
2854061da546Spatrick 
2855061da546Spatrick           debugserver_regnum_with_fixed_width_hex_register_value(
2856061da546Spatrick               ostrm, pid, tid, &g_reg_entries[reg], &reg_value);
2857061da546Spatrick         }
2858061da546Spatrick       }
2859061da546Spatrick     }
2860061da546Spatrick 
2861061da546Spatrick     if (did_exec) {
2862061da546Spatrick       ostrm << "reason:exec;";
2863061da546Spatrick     } else if (tid_stop_info.details.exception.type) {
2864061da546Spatrick       ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type
2865061da546Spatrick             << ';';
2866061da546Spatrick       ostrm << "mecount:" << std::hex
2867061da546Spatrick             << tid_stop_info.details.exception.data_count << ';';
2868061da546Spatrick       for (nub_size_t i = 0; i < tid_stop_info.details.exception.data_count;
2869061da546Spatrick            ++i)
2870061da546Spatrick         ostrm << "medata:" << std::hex
2871061da546Spatrick               << tid_stop_info.details.exception.data[i] << ';';
2872061da546Spatrick     }
2873061da546Spatrick 
2874061da546Spatrick     // Add expedited stack memory so stack backtracing doesn't need to read
2875061da546Spatrick     // anything from the
2876061da546Spatrick     // frame pointer chain.
2877061da546Spatrick     StackMemoryMap stack_mmap;
2878061da546Spatrick     ReadStackMemory(pid, tid, stack_mmap, 2);
2879061da546Spatrick     if (!stack_mmap.empty()) {
2880061da546Spatrick       for (const auto &stack_memory : stack_mmap) {
2881061da546Spatrick         ostrm << "memory:" << HEXBASE << stack_memory.first << '=';
2882061da546Spatrick         append_hex_value(ostrm, stack_memory.second.bytes,
2883061da546Spatrick                          stack_memory.second.length, false);
2884061da546Spatrick         ostrm << ';';
2885061da546Spatrick       }
2886061da546Spatrick     }
2887061da546Spatrick 
2888061da546Spatrick     return SendPacket(ostrm.str());
2889061da546Spatrick   }
2890061da546Spatrick   return SendPacket("E51");
2891061da546Spatrick }
2892061da546Spatrick 
2893061da546Spatrick /* '?'
2894061da546Spatrick  The stop reply packet - tell gdb what the status of the inferior is.
2895061da546Spatrick  Often called the questionmark_packet.  */
2896061da546Spatrick 
HandlePacket_last_signal(const char * unused)2897061da546Spatrick rnb_err_t RNBRemote::HandlePacket_last_signal(const char *unused) {
2898061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
2899061da546Spatrick     // Inferior is not yet specified/running
2900061da546Spatrick     return SendPacket("E02");
2901061da546Spatrick   }
2902061da546Spatrick 
2903061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
2904061da546Spatrick   nub_state_t pid_state = DNBProcessGetState(pid);
2905061da546Spatrick 
2906061da546Spatrick   switch (pid_state) {
2907061da546Spatrick   case eStateAttaching:
2908061da546Spatrick   case eStateLaunching:
2909061da546Spatrick   case eStateRunning:
2910061da546Spatrick   case eStateStepping:
2911061da546Spatrick   case eStateDetached:
2912061da546Spatrick     return rnb_success; // Ignore
2913061da546Spatrick 
2914061da546Spatrick   case eStateSuspended:
2915061da546Spatrick   case eStateStopped:
2916061da546Spatrick   case eStateCrashed: {
2917061da546Spatrick     nub_thread_t tid = DNBProcessGetCurrentThread(pid);
2918061da546Spatrick     // Make sure we set the current thread so g and p packets return
2919061da546Spatrick     // the data the gdb will expect.
2920061da546Spatrick     SetCurrentThread(tid);
2921061da546Spatrick 
2922061da546Spatrick     SendStopReplyPacketForThread(tid);
2923061da546Spatrick   } break;
2924061da546Spatrick 
2925061da546Spatrick   case eStateInvalid:
2926061da546Spatrick   case eStateUnloaded:
2927061da546Spatrick   case eStateExited: {
2928061da546Spatrick     char pid_exited_packet[16] = "";
2929061da546Spatrick     int pid_status = 0;
2930061da546Spatrick     // Process exited with exit status
2931061da546Spatrick     if (!DNBProcessGetExitStatus(pid, &pid_status))
2932061da546Spatrick       pid_status = 0;
2933061da546Spatrick 
2934061da546Spatrick     if (pid_status) {
2935061da546Spatrick       if (WIFEXITED(pid_status))
2936061da546Spatrick         snprintf(pid_exited_packet, sizeof(pid_exited_packet), "W%02x",
2937061da546Spatrick                  WEXITSTATUS(pid_status));
2938061da546Spatrick       else if (WIFSIGNALED(pid_status))
2939061da546Spatrick         snprintf(pid_exited_packet, sizeof(pid_exited_packet), "X%02x",
2940be691f3bSpatrick                  WTERMSIG(pid_status));
2941061da546Spatrick       else if (WIFSTOPPED(pid_status))
2942061da546Spatrick         snprintf(pid_exited_packet, sizeof(pid_exited_packet), "S%02x",
2943061da546Spatrick                  WSTOPSIG(pid_status));
2944061da546Spatrick     }
2945061da546Spatrick 
2946061da546Spatrick     // If we have an empty exit packet, lets fill one in to be safe.
2947061da546Spatrick     if (!pid_exited_packet[0]) {
2948061da546Spatrick       strlcpy(pid_exited_packet, "W00", sizeof(pid_exited_packet) - 1);
2949061da546Spatrick       pid_exited_packet[sizeof(pid_exited_packet) - 1] = '\0';
2950061da546Spatrick     }
2951061da546Spatrick 
2952061da546Spatrick     const char *exit_info = DNBProcessGetExitInfo(pid);
2953061da546Spatrick     if (exit_info != NULL && *exit_info != '\0') {
2954061da546Spatrick       std::ostringstream exit_packet;
2955061da546Spatrick       exit_packet << pid_exited_packet;
2956061da546Spatrick       exit_packet << ';';
2957061da546Spatrick       exit_packet << RAW_HEXBASE << "description";
2958061da546Spatrick       exit_packet << ':';
2959061da546Spatrick       for (size_t i = 0; exit_info[i] != '\0'; i++)
2960061da546Spatrick         exit_packet << RAWHEX8(exit_info[i]);
2961061da546Spatrick       exit_packet << ';';
2962061da546Spatrick       return SendPacket(exit_packet.str());
2963061da546Spatrick     } else
2964061da546Spatrick       return SendPacket(pid_exited_packet);
2965061da546Spatrick   } break;
2966061da546Spatrick   }
2967061da546Spatrick   return rnb_success;
2968061da546Spatrick }
2969061da546Spatrick 
HandlePacket_M(const char * p)2970061da546Spatrick rnb_err_t RNBRemote::HandlePacket_M(const char *p) {
2971061da546Spatrick   if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
2972061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short M packet");
2973061da546Spatrick   }
2974061da546Spatrick 
2975061da546Spatrick   char *c;
2976061da546Spatrick   p++;
2977061da546Spatrick   errno = 0;
2978061da546Spatrick   nub_addr_t addr = strtoull(p, &c, 16);
2979061da546Spatrick   if (errno != 0 && addr == 0) {
2980061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
2981061da546Spatrick                                   "Invalid address in M packet");
2982061da546Spatrick   }
2983061da546Spatrick   if (*c != ',') {
2984061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
2985061da546Spatrick                                   "Comma sep missing in M packet");
2986061da546Spatrick   }
2987061da546Spatrick 
2988061da546Spatrick   /* Advance 'p' to the length part of the packet.  */
2989061da546Spatrick   p += (c - p) + 1;
2990061da546Spatrick 
2991061da546Spatrick   errno = 0;
2992061da546Spatrick   unsigned long length = strtoul(p, &c, 16);
2993061da546Spatrick   if (errno != 0 && length == 0) {
2994061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
2995061da546Spatrick                                   "Invalid length in M packet");
2996061da546Spatrick   }
2997061da546Spatrick   if (length == 0) {
2998061da546Spatrick     return SendPacket("OK");
2999061da546Spatrick   }
3000061da546Spatrick 
3001061da546Spatrick   if (*c != ':') {
3002061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3003061da546Spatrick                                   "Missing colon in M packet");
3004061da546Spatrick   }
3005061da546Spatrick   /* Advance 'p' to the data part of the packet.  */
3006061da546Spatrick   p += (c - p) + 1;
3007061da546Spatrick 
3008061da546Spatrick   size_t datalen = strlen(p);
3009061da546Spatrick   if (datalen & 0x1) {
3010061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3011061da546Spatrick                                   "Uneven # of hex chars for data in M packet");
3012061da546Spatrick   }
3013061da546Spatrick   if (datalen == 0) {
3014061da546Spatrick     return SendPacket("OK");
3015061da546Spatrick   }
3016061da546Spatrick 
3017061da546Spatrick   uint8_t *buf = (uint8_t *)alloca(datalen / 2);
3018061da546Spatrick   uint8_t *i = buf;
3019061da546Spatrick 
3020061da546Spatrick   while (*p != '\0' && *(p + 1) != '\0') {
3021061da546Spatrick     char hexbuf[3];
3022061da546Spatrick     hexbuf[0] = *p;
3023061da546Spatrick     hexbuf[1] = *(p + 1);
3024061da546Spatrick     hexbuf[2] = '\0';
3025061da546Spatrick     errno = 0;
3026061da546Spatrick     uint8_t byte = strtoul(hexbuf, NULL, 16);
3027061da546Spatrick     if (errno != 0 && byte == 0) {
3028061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3029061da546Spatrick                                     "Invalid hex byte in M packet");
3030061da546Spatrick     }
3031061da546Spatrick     *i++ = byte;
3032061da546Spatrick     p += 2;
3033061da546Spatrick   }
3034061da546Spatrick 
3035061da546Spatrick   nub_size_t wrote =
3036061da546Spatrick       DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, length, buf);
3037061da546Spatrick   if (wrote != length)
3038061da546Spatrick     return SendPacket("E09");
3039061da546Spatrick   else
3040061da546Spatrick     return SendPacket("OK");
3041061da546Spatrick }
3042061da546Spatrick 
HandlePacket_m(const char * p)3043061da546Spatrick rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
3044061da546Spatrick   if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
3045061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short m packet");
3046061da546Spatrick   }
3047061da546Spatrick 
3048061da546Spatrick   char *c;
3049061da546Spatrick   p++;
3050061da546Spatrick   errno = 0;
3051061da546Spatrick   nub_addr_t addr = strtoull(p, &c, 16);
3052061da546Spatrick   if (errno != 0 && addr == 0) {
3053061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3054061da546Spatrick                                   "Invalid address in m packet");
3055061da546Spatrick   }
3056061da546Spatrick   if (*c != ',') {
3057061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3058061da546Spatrick                                   "Comma sep missing in m packet");
3059061da546Spatrick   }
3060061da546Spatrick 
3061061da546Spatrick   /* Advance 'p' to the length part of the packet.  */
3062061da546Spatrick   p += (c - p) + 1;
3063061da546Spatrick 
3064061da546Spatrick   errno = 0;
3065061da546Spatrick   auto length = strtoul(p, NULL, 16);
3066061da546Spatrick   if (errno != 0 && length == 0) {
3067061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3068061da546Spatrick                                   "Invalid length in m packet");
3069061da546Spatrick   }
3070061da546Spatrick   if (length == 0) {
3071061da546Spatrick     return SendPacket("");
3072061da546Spatrick   }
3073061da546Spatrick 
3074061da546Spatrick   std::string buf(length, '\0');
3075061da546Spatrick   if (buf.empty()) {
3076061da546Spatrick     return SendPacket("E78");
3077061da546Spatrick   }
3078061da546Spatrick   nub_size_t bytes_read =
3079061da546Spatrick       DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]);
3080061da546Spatrick   if (bytes_read == 0) {
3081061da546Spatrick     return SendPacket("E08");
3082061da546Spatrick   }
3083061da546Spatrick 
3084061da546Spatrick   // "The reply may contain fewer bytes than requested if the server was able
3085061da546Spatrick   //  to read only part of the region of memory."
3086061da546Spatrick   length = bytes_read;
3087061da546Spatrick 
3088061da546Spatrick   std::ostringstream ostrm;
3089061da546Spatrick   for (unsigned long i = 0; i < length; i++)
3090061da546Spatrick     ostrm << RAWHEX8(buf[i]);
3091061da546Spatrick   return SendPacket(ostrm.str());
3092061da546Spatrick }
3093061da546Spatrick 
3094061da546Spatrick // Read memory, sent it up as binary data.
3095061da546Spatrick // Usage:  xADDR,LEN
3096061da546Spatrick // ADDR and LEN are both base 16.
3097061da546Spatrick 
3098061da546Spatrick // Responds with 'OK' for zero-length request
3099061da546Spatrick // or
3100061da546Spatrick //
3101061da546Spatrick // DATA
3102061da546Spatrick //
3103061da546Spatrick // where DATA is the binary data payload.
3104061da546Spatrick 
HandlePacket_x(const char * p)3105061da546Spatrick rnb_err_t RNBRemote::HandlePacket_x(const char *p) {
3106061da546Spatrick   if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
3107061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short X packet");
3108061da546Spatrick   }
3109061da546Spatrick 
3110061da546Spatrick   char *c;
3111061da546Spatrick   p++;
3112061da546Spatrick   errno = 0;
3113061da546Spatrick   nub_addr_t addr = strtoull(p, &c, 16);
3114061da546Spatrick   if (errno != 0) {
3115061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3116061da546Spatrick                                   "Invalid address in X packet");
3117061da546Spatrick   }
3118061da546Spatrick   if (*c != ',') {
3119061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3120061da546Spatrick                                   "Comma sep missing in X packet");
3121061da546Spatrick   }
3122061da546Spatrick 
3123061da546Spatrick   /* Advance 'p' to the number of bytes to be read.  */
3124061da546Spatrick   p += (c - p) + 1;
3125061da546Spatrick 
3126061da546Spatrick   errno = 0;
3127061da546Spatrick   auto length = strtoul(p, NULL, 16);
3128061da546Spatrick   if (errno != 0) {
3129061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3130061da546Spatrick                                   "Invalid length in x packet");
3131061da546Spatrick   }
3132061da546Spatrick 
3133061da546Spatrick   // zero length read means this is a test of whether that packet is implemented
3134061da546Spatrick   // or not.
3135061da546Spatrick   if (length == 0) {
3136061da546Spatrick     return SendPacket("OK");
3137061da546Spatrick   }
3138061da546Spatrick 
3139061da546Spatrick   std::vector<uint8_t> buf(length);
3140061da546Spatrick 
3141061da546Spatrick   if (buf.capacity() != length) {
3142061da546Spatrick     return SendPacket("E79");
3143061da546Spatrick   }
3144061da546Spatrick   nub_size_t bytes_read =
3145061da546Spatrick       DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]);
3146061da546Spatrick   if (bytes_read == 0) {
3147061da546Spatrick     return SendPacket("E80");
3148061da546Spatrick   }
3149061da546Spatrick 
3150061da546Spatrick   std::vector<uint8_t> buf_quoted;
3151061da546Spatrick   buf_quoted.reserve(bytes_read + 30);
3152061da546Spatrick   for (nub_size_t i = 0; i < bytes_read; i++) {
3153061da546Spatrick     if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') {
3154061da546Spatrick       buf_quoted.push_back(0x7d);
3155061da546Spatrick       buf_quoted.push_back(buf[i] ^ 0x20);
3156061da546Spatrick     } else {
3157061da546Spatrick       buf_quoted.push_back(buf[i]);
3158061da546Spatrick     }
3159061da546Spatrick   }
3160061da546Spatrick   length = buf_quoted.size();
3161061da546Spatrick 
3162061da546Spatrick   std::ostringstream ostrm;
3163061da546Spatrick   for (unsigned long i = 0; i < length; i++)
3164061da546Spatrick     ostrm << buf_quoted[i];
3165061da546Spatrick 
3166061da546Spatrick   return SendPacket(ostrm.str());
3167061da546Spatrick }
3168061da546Spatrick 
HandlePacket_X(const char * p)3169061da546Spatrick rnb_err_t RNBRemote::HandlePacket_X(const char *p) {
3170061da546Spatrick   if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
3171061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short X packet");
3172061da546Spatrick   }
3173061da546Spatrick 
3174061da546Spatrick   char *c;
3175061da546Spatrick   p++;
3176061da546Spatrick   errno = 0;
3177061da546Spatrick   nub_addr_t addr = strtoull(p, &c, 16);
3178061da546Spatrick   if (errno != 0 && addr == 0) {
3179061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3180061da546Spatrick                                   "Invalid address in X packet");
3181061da546Spatrick   }
3182061da546Spatrick   if (*c != ',') {
3183061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3184061da546Spatrick                                   "Comma sep missing in X packet");
3185061da546Spatrick   }
3186061da546Spatrick 
3187061da546Spatrick   /* Advance 'p' to the length part of the packet.  NB this is the length of the
3188061da546Spatrick      packet
3189061da546Spatrick      including any escaped chars.  The data payload may be a little bit smaller
3190061da546Spatrick      after
3191061da546Spatrick      decoding.  */
3192061da546Spatrick   p += (c - p) + 1;
3193061da546Spatrick 
3194061da546Spatrick   errno = 0;
3195061da546Spatrick   auto length = strtoul(p, NULL, 16);
3196061da546Spatrick   if (errno != 0 && length == 0) {
3197061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3198061da546Spatrick                                   "Invalid length in X packet");
3199061da546Spatrick   }
3200061da546Spatrick 
3201061da546Spatrick   // I think gdb sends a zero length write request to test whether this
3202061da546Spatrick   // packet is accepted.
3203061da546Spatrick   if (length == 0) {
3204061da546Spatrick     return SendPacket("OK");
3205061da546Spatrick   }
3206061da546Spatrick 
3207061da546Spatrick   std::vector<uint8_t> data = decode_binary_data(c, -1);
3208061da546Spatrick   std::vector<uint8_t>::const_iterator it;
3209061da546Spatrick   uint8_t *buf = (uint8_t *)alloca(data.size());
3210061da546Spatrick   uint8_t *i = buf;
3211061da546Spatrick   for (it = data.begin(); it != data.end(); ++it) {
3212061da546Spatrick     *i++ = *it;
3213061da546Spatrick   }
3214061da546Spatrick 
3215061da546Spatrick   nub_size_t wrote =
3216061da546Spatrick       DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, data.size(), buf);
3217061da546Spatrick   if (wrote != data.size())
3218061da546Spatrick     return SendPacket("E08");
3219061da546Spatrick   return SendPacket("OK");
3220061da546Spatrick }
3221061da546Spatrick 
3222061da546Spatrick /* 'g' -- read registers
3223061da546Spatrick  Get the contents of the registers for the current thread,
3224061da546Spatrick  send them to gdb.
3225061da546Spatrick  Should the setting of the Hg packet determine which thread's registers
3226061da546Spatrick  are returned?  */
3227061da546Spatrick 
HandlePacket_g(const char * p)3228061da546Spatrick rnb_err_t RNBRemote::HandlePacket_g(const char *p) {
3229061da546Spatrick   std::ostringstream ostrm;
3230061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
3231061da546Spatrick     return SendPacket("E11");
3232061da546Spatrick   }
3233061da546Spatrick 
3234061da546Spatrick   if (g_num_reg_entries == 0)
3235061da546Spatrick     InitializeRegisters();
3236061da546Spatrick 
3237061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
3238061da546Spatrick   nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p + 1);
3239061da546Spatrick   if (tid == INVALID_NUB_THREAD)
3240061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3241061da546Spatrick                                   "No thread specified in p packet");
3242061da546Spatrick 
3243061da546Spatrick   // Get the register context size first by calling with NULL buffer
3244061da546Spatrick   nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
3245061da546Spatrick   if (reg_ctx_size) {
3246061da546Spatrick     // Now allocate enough space for the entire register context
3247061da546Spatrick     std::vector<uint8_t> reg_ctx;
3248061da546Spatrick     reg_ctx.resize(reg_ctx_size);
3249061da546Spatrick     // Now read the register context
3250061da546Spatrick     reg_ctx_size =
3251061da546Spatrick         DNBThreadGetRegisterContext(pid, tid, &reg_ctx[0], reg_ctx.size());
3252061da546Spatrick     if (reg_ctx_size) {
3253061da546Spatrick       append_hex_value(ostrm, reg_ctx.data(), reg_ctx.size(), false);
3254061da546Spatrick       return SendPacket(ostrm.str());
3255061da546Spatrick     }
3256061da546Spatrick   }
3257061da546Spatrick   return SendPacket("E74");
3258061da546Spatrick }
3259061da546Spatrick 
3260061da546Spatrick /* 'G XXX...' -- write registers
3261061da546Spatrick  How is the thread for these specified, beyond "the current thread"?
3262061da546Spatrick  Does gdb actually use the Hg packet to set this?  */
3263061da546Spatrick 
HandlePacket_G(const char * p)3264061da546Spatrick rnb_err_t RNBRemote::HandlePacket_G(const char *p) {
3265061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
3266061da546Spatrick     return SendPacket("E11");
3267061da546Spatrick   }
3268061da546Spatrick 
3269061da546Spatrick   if (g_num_reg_entries == 0)
3270061da546Spatrick     InitializeRegisters();
3271061da546Spatrick 
3272061da546Spatrick   StdStringExtractor packet(p);
3273061da546Spatrick   packet.SetFilePos(1); // Skip the 'G'
3274061da546Spatrick 
3275061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
3276061da546Spatrick   nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
3277061da546Spatrick   if (tid == INVALID_NUB_THREAD)
3278061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3279061da546Spatrick                                   "No thread specified in p packet");
3280061da546Spatrick 
3281061da546Spatrick   // Get the register context size first by calling with NULL buffer
3282061da546Spatrick   nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
3283061da546Spatrick   if (reg_ctx_size) {
3284061da546Spatrick     // Now allocate enough space for the entire register context
3285061da546Spatrick     std::vector<uint8_t> reg_ctx;
3286061da546Spatrick     reg_ctx.resize(reg_ctx_size);
3287061da546Spatrick 
3288061da546Spatrick     const nub_size_t bytes_extracted =
3289061da546Spatrick         packet.GetHexBytes(&reg_ctx[0], reg_ctx.size(), 0xcc);
3290061da546Spatrick     if (bytes_extracted == reg_ctx.size()) {
3291061da546Spatrick       // Now write the register context
3292061da546Spatrick       reg_ctx_size =
3293061da546Spatrick           DNBThreadSetRegisterContext(pid, tid, reg_ctx.data(), reg_ctx.size());
3294061da546Spatrick       if (reg_ctx_size == reg_ctx.size())
3295061da546Spatrick         return SendPacket("OK");
3296061da546Spatrick       else
3297061da546Spatrick         return SendPacket("E55");
3298061da546Spatrick     } else {
3299061da546Spatrick       DNBLogError("RNBRemote::HandlePacket_G(%s): extracted %llu of %llu "
3300061da546Spatrick                   "bytes, size mismatch\n",
3301061da546Spatrick                   p, (uint64_t)bytes_extracted, (uint64_t)reg_ctx_size);
3302061da546Spatrick       return SendPacket("E64");
3303061da546Spatrick     }
3304061da546Spatrick   }
3305061da546Spatrick   return SendPacket("E65");
3306061da546Spatrick }
3307061da546Spatrick 
RNBRemoteShouldCancelCallback(void * not_used)3308061da546Spatrick static bool RNBRemoteShouldCancelCallback(void *not_used) {
3309061da546Spatrick   RNBRemoteSP remoteSP(g_remoteSP);
3310061da546Spatrick   if (remoteSP.get() != NULL) {
3311061da546Spatrick     RNBRemote *remote = remoteSP.get();
3312061da546Spatrick     return !remote->Comm().IsConnected();
3313061da546Spatrick   }
3314061da546Spatrick   return true;
3315061da546Spatrick }
3316061da546Spatrick 
3317061da546Spatrick // FORMAT: _MXXXXXX,PPP
3318061da546Spatrick //      XXXXXX: big endian hex chars
3319061da546Spatrick //      PPP: permissions can be any combo of r w x chars
3320061da546Spatrick //
3321061da546Spatrick // RESPONSE: XXXXXX
3322061da546Spatrick //      XXXXXX: hex address of the newly allocated memory
3323061da546Spatrick //      EXX: error code
3324061da546Spatrick //
3325061da546Spatrick // EXAMPLES:
3326061da546Spatrick //      _M123000,rw
3327061da546Spatrick //      _M123000,rwx
3328061da546Spatrick //      _M123000,xw
3329061da546Spatrick 
HandlePacket_AllocateMemory(const char * p)3330061da546Spatrick rnb_err_t RNBRemote::HandlePacket_AllocateMemory(const char *p) {
3331061da546Spatrick   StdStringExtractor packet(p);
3332061da546Spatrick   packet.SetFilePos(2); // Skip the "_M"
3333061da546Spatrick 
3334061da546Spatrick   nub_addr_t size = packet.GetHexMaxU64(StdStringExtractor::BigEndian, 0);
3335061da546Spatrick   if (size != 0) {
3336061da546Spatrick     if (packet.GetChar() == ',') {
3337061da546Spatrick       uint32_t permissions = 0;
3338061da546Spatrick       char ch;
3339061da546Spatrick       bool success = true;
3340061da546Spatrick       while (success && (ch = packet.GetChar()) != '\0') {
3341061da546Spatrick         switch (ch) {
3342061da546Spatrick         case 'r':
3343061da546Spatrick           permissions |= eMemoryPermissionsReadable;
3344061da546Spatrick           break;
3345061da546Spatrick         case 'w':
3346061da546Spatrick           permissions |= eMemoryPermissionsWritable;
3347061da546Spatrick           break;
3348061da546Spatrick         case 'x':
3349061da546Spatrick           permissions |= eMemoryPermissionsExecutable;
3350061da546Spatrick           break;
3351061da546Spatrick         default:
3352061da546Spatrick           success = false;
3353061da546Spatrick           break;
3354061da546Spatrick         }
3355061da546Spatrick       }
3356061da546Spatrick 
3357061da546Spatrick       if (success) {
3358061da546Spatrick         nub_addr_t addr =
3359061da546Spatrick             DNBProcessMemoryAllocate(m_ctx.ProcessID(), size, permissions);
3360061da546Spatrick         if (addr != INVALID_NUB_ADDRESS) {
3361061da546Spatrick           std::ostringstream ostrm;
3362061da546Spatrick           ostrm << RAW_HEXBASE << addr;
3363061da546Spatrick           return SendPacket(ostrm.str());
3364061da546Spatrick         }
3365061da546Spatrick       }
3366061da546Spatrick     }
3367061da546Spatrick   }
3368061da546Spatrick   return SendPacket("E53");
3369061da546Spatrick }
3370061da546Spatrick 
3371061da546Spatrick // FORMAT: _mXXXXXX
3372061da546Spatrick //      XXXXXX: address that was previously allocated
3373061da546Spatrick //
3374061da546Spatrick // RESPONSE: XXXXXX
3375061da546Spatrick //      OK: address was deallocated
3376061da546Spatrick //      EXX: error code
3377061da546Spatrick //
3378061da546Spatrick // EXAMPLES:
3379061da546Spatrick //      _m123000
3380061da546Spatrick 
HandlePacket_DeallocateMemory(const char * p)3381061da546Spatrick rnb_err_t RNBRemote::HandlePacket_DeallocateMemory(const char *p) {
3382061da546Spatrick   StdStringExtractor packet(p);
3383061da546Spatrick   packet.SetFilePos(2); // Skip the "_m"
3384061da546Spatrick   nub_addr_t addr =
3385061da546Spatrick       packet.GetHexMaxU64(StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS);
3386061da546Spatrick 
3387061da546Spatrick   if (addr != INVALID_NUB_ADDRESS) {
3388061da546Spatrick     if (DNBProcessMemoryDeallocate(m_ctx.ProcessID(), addr))
3389061da546Spatrick       return SendPacket("OK");
3390061da546Spatrick   }
3391061da546Spatrick   return SendPacket("E54");
3392061da546Spatrick }
3393061da546Spatrick 
3394061da546Spatrick // FORMAT: QSaveRegisterState;thread:TTTT;  (when thread suffix is supported)
3395061da546Spatrick // FORMAT: QSaveRegisterState               (when thread suffix is NOT
3396061da546Spatrick // supported)
3397061da546Spatrick //      TTTT: thread ID in hex
3398061da546Spatrick //
3399061da546Spatrick // RESPONSE:
3400061da546Spatrick //      SAVEID: Where SAVEID is a decimal number that represents the save ID
3401061da546Spatrick //              that can be passed back into a "QRestoreRegisterState" packet
3402061da546Spatrick //      EXX: error code
3403061da546Spatrick //
3404061da546Spatrick // EXAMPLES:
3405061da546Spatrick //      QSaveRegisterState;thread:1E34;     (when thread suffix is supported)
3406061da546Spatrick //      QSaveRegisterState                  (when thread suffix is NOT
3407061da546Spatrick //      supported)
3408061da546Spatrick 
HandlePacket_SaveRegisterState(const char * p)3409061da546Spatrick rnb_err_t RNBRemote::HandlePacket_SaveRegisterState(const char *p) {
3410061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
3411061da546Spatrick   nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
3412061da546Spatrick   if (tid == INVALID_NUB_THREAD) {
3413061da546Spatrick     if (m_thread_suffix_supported)
3414061da546Spatrick       return HandlePacket_ILLFORMED(
3415061da546Spatrick           __FILE__, __LINE__, p,
3416061da546Spatrick           "No thread specified in QSaveRegisterState packet");
3417061da546Spatrick     else
3418061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3419061da546Spatrick                                     "No thread was is set with the Hg packet");
3420061da546Spatrick   }
3421061da546Spatrick 
3422061da546Spatrick   // Get the register context size first by calling with NULL buffer
3423061da546Spatrick   const uint32_t save_id = DNBThreadSaveRegisterState(pid, tid);
3424061da546Spatrick   if (save_id != 0) {
3425061da546Spatrick     char response[64];
3426061da546Spatrick     snprintf(response, sizeof(response), "%u", save_id);
3427061da546Spatrick     return SendPacket(response);
3428061da546Spatrick   } else {
3429061da546Spatrick     return SendPacket("E75");
3430061da546Spatrick   }
3431061da546Spatrick }
3432061da546Spatrick // FORMAT: QRestoreRegisterState:SAVEID;thread:TTTT;  (when thread suffix is
3433061da546Spatrick // supported)
3434061da546Spatrick // FORMAT: QRestoreRegisterState:SAVEID               (when thread suffix is NOT
3435061da546Spatrick // supported)
3436061da546Spatrick //      TTTT: thread ID in hex
3437061da546Spatrick //      SAVEID: a decimal number that represents the save ID that was
3438061da546Spatrick //              returned from a call to "QSaveRegisterState"
3439061da546Spatrick //
3440061da546Spatrick // RESPONSE:
3441061da546Spatrick //      OK: successfully restored registers for the specified thread
3442061da546Spatrick //      EXX: error code
3443061da546Spatrick //
3444061da546Spatrick // EXAMPLES:
3445061da546Spatrick //      QRestoreRegisterState:1;thread:1E34;     (when thread suffix is
3446061da546Spatrick //      supported)
3447061da546Spatrick //      QRestoreRegisterState:1                  (when thread suffix is NOT
3448061da546Spatrick //      supported)
3449061da546Spatrick 
HandlePacket_RestoreRegisterState(const char * p)3450061da546Spatrick rnb_err_t RNBRemote::HandlePacket_RestoreRegisterState(const char *p) {
3451061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
3452061da546Spatrick   nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
3453061da546Spatrick   if (tid == INVALID_NUB_THREAD) {
3454061da546Spatrick     if (m_thread_suffix_supported)
3455061da546Spatrick       return HandlePacket_ILLFORMED(
3456061da546Spatrick           __FILE__, __LINE__, p,
3457061da546Spatrick           "No thread specified in QSaveRegisterState packet");
3458061da546Spatrick     else
3459061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3460061da546Spatrick                                     "No thread was is set with the Hg packet");
3461061da546Spatrick   }
3462061da546Spatrick 
3463061da546Spatrick   StdStringExtractor packet(p);
3464061da546Spatrick   packet.SetFilePos(
3465061da546Spatrick       strlen("QRestoreRegisterState:")); // Skip the "QRestoreRegisterState:"
3466061da546Spatrick   const uint32_t save_id = packet.GetU32(0);
3467061da546Spatrick 
3468061da546Spatrick   if (save_id != 0) {
3469061da546Spatrick     // Get the register context size first by calling with NULL buffer
3470061da546Spatrick     if (DNBThreadRestoreRegisterState(pid, tid, save_id))
3471061da546Spatrick       return SendPacket("OK");
3472061da546Spatrick     else
3473061da546Spatrick       return SendPacket("E77");
3474061da546Spatrick   }
3475061da546Spatrick   return SendPacket("E76");
3476061da546Spatrick }
3477061da546Spatrick 
GetProcessNameFrom_vAttach(const char * & p,std::string & attach_name)3478061da546Spatrick static bool GetProcessNameFrom_vAttach(const char *&p,
3479061da546Spatrick                                        std::string &attach_name) {
3480061da546Spatrick   bool return_val = true;
3481061da546Spatrick   while (*p != '\0') {
3482061da546Spatrick     char smallbuf[3];
3483061da546Spatrick     smallbuf[0] = *p;
3484061da546Spatrick     smallbuf[1] = *(p + 1);
3485061da546Spatrick     smallbuf[2] = '\0';
3486061da546Spatrick 
3487061da546Spatrick     errno = 0;
3488061da546Spatrick     int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
3489061da546Spatrick     if (errno != 0 && ch == 0) {
3490061da546Spatrick       return_val = false;
3491061da546Spatrick       break;
3492061da546Spatrick     }
3493061da546Spatrick 
3494061da546Spatrick     attach_name.push_back(ch);
3495061da546Spatrick     p += 2;
3496061da546Spatrick   }
3497061da546Spatrick   return return_val;
3498061da546Spatrick }
3499061da546Spatrick 
HandlePacket_qSupported(const char * p)3500061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
3501061da546Spatrick   uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
3502061da546Spatrick                                          // size--debugger can always use less
3503061da546Spatrick   char buf[256];
3504*f6aab3d8Srobert   snprintf(buf, sizeof(buf),
3505*f6aab3d8Srobert            "qXfer:features:read+;PacketSize=%x;qEcho+;native-signals+",
3506061da546Spatrick            max_packet_size);
3507061da546Spatrick 
3508061da546Spatrick   bool enable_compression = false;
3509061da546Spatrick   (void)enable_compression;
3510061da546Spatrick 
3511061da546Spatrick #if (defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1) \
3512061da546Spatrick     || (defined (TARGET_OS_IOS) && TARGET_OS_IOS == 1) \
3513061da546Spatrick     || (defined (TARGET_OS_TV) && TARGET_OS_TV == 1) \
3514061da546Spatrick     || (defined (TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1)
3515061da546Spatrick   enable_compression = true;
3516061da546Spatrick #endif
3517061da546Spatrick 
3518061da546Spatrick   if (enable_compression) {
3519061da546Spatrick     strcat(buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;"
3520061da546Spatrick                 "DefaultCompressionMinSize=");
3521061da546Spatrick     char numbuf[16];
3522061da546Spatrick     snprintf(numbuf, sizeof(numbuf), "%zu", m_compression_minsize);
3523061da546Spatrick     numbuf[sizeof(numbuf) - 1] = '\0';
3524061da546Spatrick     strcat(buf, numbuf);
3525061da546Spatrick   }
3526061da546Spatrick 
3527061da546Spatrick   return SendPacket(buf);
3528061da546Spatrick }
3529061da546Spatrick 
process_does_not_exist(nub_process_t pid)3530dda28197Spatrick static bool process_does_not_exist (nub_process_t pid) {
3531dda28197Spatrick   std::vector<struct kinfo_proc> proc_infos;
3532dda28197Spatrick   DNBGetAllInfos (proc_infos);
3533dda28197Spatrick   const size_t infos_size = proc_infos.size();
3534dda28197Spatrick   for (size_t i = 0; i < infos_size; i++)
3535dda28197Spatrick     if (proc_infos[i].kp_proc.p_pid == pid)
3536dda28197Spatrick       return false;
3537dda28197Spatrick 
3538dda28197Spatrick   return true; // process does not exist
3539dda28197Spatrick }
3540dda28197Spatrick 
3541dda28197Spatrick // my_uid and process_uid are only initialized if this function
3542dda28197Spatrick // returns true -- that there was a uid mismatch -- and those
3543dda28197Spatrick // id's may want to be used in the error message.
3544dda28197Spatrick //
3545dda28197Spatrick // NOTE: this should only be called after process_does_not_exist().
3546dda28197Spatrick // This sysctl will return uninitialized data if we ask for a pid
3547dda28197Spatrick // that doesn't exist.  The alternative would be to fetch all
3548dda28197Spatrick // processes and step through to find the one we're looking for
3549dda28197Spatrick // (as process_does_not_exist() does).
attach_failed_due_to_uid_mismatch(nub_process_t pid,uid_t & my_uid,uid_t & process_uid)3550dda28197Spatrick static bool attach_failed_due_to_uid_mismatch (nub_process_t pid,
3551dda28197Spatrick                                                uid_t &my_uid,
3552dda28197Spatrick                                                uid_t &process_uid) {
3553dda28197Spatrick   struct kinfo_proc kinfo;
3554dda28197Spatrick   int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
3555dda28197Spatrick   size_t len = sizeof(struct kinfo_proc);
3556dda28197Spatrick   if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kinfo, &len, NULL, 0) != 0) {
3557dda28197Spatrick     return false; // pid doesn't exist? can't check uid mismatch - it was fine
3558dda28197Spatrick   }
3559dda28197Spatrick   my_uid = geteuid();
3560dda28197Spatrick   if (my_uid == 0)
3561dda28197Spatrick     return false; // if we're root, attach didn't fail because of uid mismatch
3562dda28197Spatrick   process_uid = kinfo.kp_eproc.e_ucred.cr_uid;
3563dda28197Spatrick 
3564dda28197Spatrick   // If my uid != the process' uid, then the attach probably failed because
3565dda28197Spatrick   // of that.
3566dda28197Spatrick   if (my_uid != process_uid)
3567dda28197Spatrick     return true;
3568dda28197Spatrick   else
3569dda28197Spatrick     return false;
3570dda28197Spatrick }
3571dda28197Spatrick 
3572dda28197Spatrick // NOTE: this should only be called after process_does_not_exist().
3573dda28197Spatrick // This sysctl will return uninitialized data if we ask for a pid
3574dda28197Spatrick // that doesn't exist.  The alternative would be to fetch all
3575dda28197Spatrick // processes and step through to find the one we're looking for
3576dda28197Spatrick // (as process_does_not_exist() does).
process_is_already_being_debugged(nub_process_t pid)3577dda28197Spatrick static bool process_is_already_being_debugged (nub_process_t pid) {
3578dda28197Spatrick   struct kinfo_proc kinfo;
3579dda28197Spatrick   int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
3580dda28197Spatrick   size_t len = sizeof(struct kinfo_proc);
3581dda28197Spatrick   if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kinfo, &len, NULL, 0) != 0) {
3582dda28197Spatrick     return false; // pid doesn't exist? well, it's not being debugged...
3583dda28197Spatrick   }
3584dda28197Spatrick   if (kinfo.kp_proc.p_flag & P_TRACED)
3585dda28197Spatrick     return true; // is being debugged already
3586dda28197Spatrick   else
3587dda28197Spatrick     return false;
3588dda28197Spatrick }
3589dda28197Spatrick 
3590dda28197Spatrick // Test if this current login session has a connection to the
3591dda28197Spatrick // window server (if it does not have that access, it cannot ask
3592dda28197Spatrick // for debug permission by popping up a dialog box and attach
3593dda28197Spatrick // may fail outright).
login_session_has_gui_access()3594dda28197Spatrick static bool login_session_has_gui_access () {
3595dda28197Spatrick   // I believe this API only works on macOS.
3596dda28197Spatrick #if TARGET_OS_OSX == 0
3597dda28197Spatrick   return true;
3598dda28197Spatrick #else
3599dda28197Spatrick   auditinfo_addr_t info;
3600dda28197Spatrick   getaudit_addr(&info, sizeof(info));
3601dda28197Spatrick   if (info.ai_flags & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS)
3602dda28197Spatrick     return true;
3603dda28197Spatrick   else
3604dda28197Spatrick     return false;
3605dda28197Spatrick #endif
3606dda28197Spatrick }
3607dda28197Spatrick 
3608dda28197Spatrick // Checking for
3609dda28197Spatrick //
3610dda28197Spatrick //  {
3611dda28197Spatrick //    'class' : 'rule',
3612dda28197Spatrick //    'comment' : 'For use by Apple.  WARNING: administrators are advised
3613dda28197Spatrick //              not to modify this right.',
3614dda28197Spatrick //    'k-of-n' : '1',
3615dda28197Spatrick //    'rule' : [
3616dda28197Spatrick //      'is-admin',
3617dda28197Spatrick //      'is-developer',
3618dda28197Spatrick //      'authenticate-developer'
3619dda28197Spatrick //    ]
3620dda28197Spatrick //  }
3621dda28197Spatrick //
3622dda28197Spatrick // $ security authorizationdb read system.privilege.taskport.debug
3623dda28197Spatrick 
developer_mode_enabled()3624dda28197Spatrick static bool developer_mode_enabled () {
3625dda28197Spatrick   // This API only exists on macOS.
3626dda28197Spatrick #if TARGET_OS_OSX == 0
3627dda28197Spatrick   return true;
3628dda28197Spatrick #else
3629dda28197Spatrick  CFDictionaryRef currentRightDict = NULL;
3630dda28197Spatrick  const char *debug_right = "system.privilege.taskport.debug";
3631dda28197Spatrick  // caller must free dictionary initialized by the following
3632dda28197Spatrick  OSStatus status = AuthorizationRightGet(debug_right, &currentRightDict);
3633dda28197Spatrick  if (status != errAuthorizationSuccess) {
3634dda28197Spatrick    // could not check authorization
3635dda28197Spatrick    return true;
3636dda28197Spatrick  }
3637dda28197Spatrick 
3638dda28197Spatrick  bool devmode_enabled = true;
3639dda28197Spatrick 
3640dda28197Spatrick  if (!CFDictionaryContainsKey(currentRightDict, CFSTR("k-of-n"))) {
3641dda28197Spatrick    devmode_enabled = false;
3642dda28197Spatrick  } else {
3643dda28197Spatrick    CFNumberRef item = (CFNumberRef) CFDictionaryGetValue(currentRightDict, CFSTR("k-of-n"));
3644dda28197Spatrick    if (item && CFGetTypeID(item) == CFNumberGetTypeID()) {
3645dda28197Spatrick       int64_t num = 0;
3646dda28197Spatrick       ::CFNumberGetValue(item, kCFNumberSInt64Type, &num);
3647dda28197Spatrick       if (num != 1) {
3648dda28197Spatrick         devmode_enabled = false;
3649dda28197Spatrick       }
3650dda28197Spatrick    } else {
3651dda28197Spatrick      devmode_enabled = false;
3652dda28197Spatrick    }
3653dda28197Spatrick  }
3654dda28197Spatrick 
3655dda28197Spatrick  if (!CFDictionaryContainsKey(currentRightDict, CFSTR("class"))) {
3656dda28197Spatrick    devmode_enabled = false;
3657dda28197Spatrick  } else {
3658dda28197Spatrick    CFStringRef item = (CFStringRef) CFDictionaryGetValue(currentRightDict, CFSTR("class"));
3659dda28197Spatrick    if (item && CFGetTypeID(item) == CFStringGetTypeID()) {
3660dda28197Spatrick      char tmpbuf[128];
3661dda28197Spatrick      if (CFStringGetCString (item, tmpbuf, sizeof(tmpbuf), CFStringGetSystemEncoding())) {
3662dda28197Spatrick        tmpbuf[sizeof (tmpbuf) - 1] = '\0';
3663dda28197Spatrick        if (strcmp (tmpbuf, "rule") != 0) {
3664dda28197Spatrick          devmode_enabled = false;
3665dda28197Spatrick        }
3666dda28197Spatrick      } else {
3667dda28197Spatrick        devmode_enabled = false;
3668dda28197Spatrick      }
3669dda28197Spatrick    } else {
3670dda28197Spatrick      devmode_enabled = false;
3671dda28197Spatrick    }
3672dda28197Spatrick  }
3673dda28197Spatrick 
3674dda28197Spatrick  if (!CFDictionaryContainsKey(currentRightDict, CFSTR("rule"))) {
3675dda28197Spatrick    devmode_enabled = false;
3676dda28197Spatrick  } else {
3677dda28197Spatrick    CFArrayRef item = (CFArrayRef) CFDictionaryGetValue(currentRightDict, CFSTR("rule"));
3678dda28197Spatrick    if (item && CFGetTypeID(item) == CFArrayGetTypeID()) {
3679dda28197Spatrick      int count = ::CFArrayGetCount(item);
3680dda28197Spatrick       CFRange range = CFRangeMake (0, count);
3681dda28197Spatrick      if (!::CFArrayContainsValue (item, range, CFSTR("is-admin")))
3682dda28197Spatrick        devmode_enabled = false;
3683dda28197Spatrick      if (!::CFArrayContainsValue (item, range, CFSTR("is-developer")))
3684dda28197Spatrick        devmode_enabled = false;
3685dda28197Spatrick      if (!::CFArrayContainsValue (item, range, CFSTR("authenticate-developer")))
3686dda28197Spatrick        devmode_enabled = false;
3687dda28197Spatrick    } else {
3688dda28197Spatrick      devmode_enabled = false;
3689dda28197Spatrick    }
3690dda28197Spatrick  }
3691dda28197Spatrick  ::CFRelease(currentRightDict);
3692dda28197Spatrick 
3693dda28197Spatrick  return devmode_enabled;
3694dda28197Spatrick #endif // TARGET_OS_OSX
3695dda28197Spatrick }
3696dda28197Spatrick 
3697061da546Spatrick /*
3698061da546Spatrick  vAttach;pid
3699061da546Spatrick 
3700061da546Spatrick  Attach to a new process with the specified process ID. pid is a hexadecimal
3701061da546Spatrick  integer
3702061da546Spatrick  identifying the process. If the stub is currently controlling a process, it is
3703061da546Spatrick  killed. The attached process is stopped.This packet is only available in
3704061da546Spatrick  extended
3705061da546Spatrick  mode (see extended mode).
3706061da546Spatrick 
3707061da546Spatrick  Reply:
3708061da546Spatrick  "ENN"                      for an error
3709061da546Spatrick  "Any Stop Reply Packet"     for success
3710061da546Spatrick  */
3711061da546Spatrick 
HandlePacket_v(const char * p)3712061da546Spatrick rnb_err_t RNBRemote::HandlePacket_v(const char *p) {
3713061da546Spatrick   if (strcmp(p, "vCont;c") == 0) {
3714061da546Spatrick     // Simple continue
3715061da546Spatrick     return RNBRemote::HandlePacket_c("c");
3716061da546Spatrick   } else if (strcmp(p, "vCont;s") == 0) {
3717061da546Spatrick     // Simple step
3718061da546Spatrick     return RNBRemote::HandlePacket_s("s");
3719061da546Spatrick   } else if (strstr(p, "vCont") == p) {
3720061da546Spatrick     DNBThreadResumeActions thread_actions;
3721061da546Spatrick     char *c = const_cast<char *>(p += strlen("vCont"));
3722061da546Spatrick     char *c_end = c + strlen(c);
3723061da546Spatrick     if (*c == '?')
3724061da546Spatrick       return SendPacket("vCont;c;C;s;S");
3725061da546Spatrick 
3726061da546Spatrick     while (c < c_end && *c == ';') {
3727061da546Spatrick       ++c; // Skip the semi-colon
3728061da546Spatrick       DNBThreadResumeAction thread_action;
3729061da546Spatrick       thread_action.tid = INVALID_NUB_THREAD;
3730061da546Spatrick       thread_action.state = eStateInvalid;
3731061da546Spatrick       thread_action.signal = 0;
3732061da546Spatrick       thread_action.addr = INVALID_NUB_ADDRESS;
3733061da546Spatrick 
3734061da546Spatrick       char action = *c++;
3735061da546Spatrick 
3736061da546Spatrick       switch (action) {
3737061da546Spatrick       case 'C':
3738061da546Spatrick         errno = 0;
3739061da546Spatrick         thread_action.signal = static_cast<int>(strtoul(c, &c, 16));
3740061da546Spatrick         if (errno != 0)
3741061da546Spatrick           return HandlePacket_ILLFORMED(
3742061da546Spatrick               __FILE__, __LINE__, p, "Could not parse signal in vCont packet");
3743061da546Spatrick       // Fall through to next case...
3744061da546Spatrick         [[clang::fallthrough]];
3745061da546Spatrick       case 'c':
3746061da546Spatrick         // Continue
3747061da546Spatrick         thread_action.state = eStateRunning;
3748061da546Spatrick         break;
3749061da546Spatrick 
3750061da546Spatrick       case 'S':
3751061da546Spatrick         errno = 0;
3752061da546Spatrick         thread_action.signal = static_cast<int>(strtoul(c, &c, 16));
3753061da546Spatrick         if (errno != 0)
3754061da546Spatrick           return HandlePacket_ILLFORMED(
3755061da546Spatrick               __FILE__, __LINE__, p, "Could not parse signal in vCont packet");
3756061da546Spatrick       // Fall through to next case...
3757061da546Spatrick         [[clang::fallthrough]];
3758061da546Spatrick       case 's':
3759061da546Spatrick         // Step
3760061da546Spatrick         thread_action.state = eStateStepping;
3761061da546Spatrick         break;
3762061da546Spatrick 
3763061da546Spatrick       default:
3764061da546Spatrick         HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3765061da546Spatrick                                "Unsupported action in vCont packet");
3766061da546Spatrick         break;
3767061da546Spatrick       }
3768061da546Spatrick       if (*c == ':') {
3769061da546Spatrick         errno = 0;
3770061da546Spatrick         thread_action.tid = strtoul(++c, &c, 16);
3771061da546Spatrick         if (errno != 0)
3772061da546Spatrick           return HandlePacket_ILLFORMED(
3773061da546Spatrick               __FILE__, __LINE__, p,
3774061da546Spatrick               "Could not parse thread number in vCont packet");
3775061da546Spatrick       }
3776061da546Spatrick 
3777061da546Spatrick       thread_actions.Append(thread_action);
3778061da546Spatrick     }
3779061da546Spatrick 
3780061da546Spatrick     // If a default action for all other threads wasn't mentioned
3781061da546Spatrick     // then we should stop the threads
3782061da546Spatrick     thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
3783061da546Spatrick     DNBProcessResume(m_ctx.ProcessID(), thread_actions.GetFirst(),
3784061da546Spatrick                      thread_actions.GetSize());
3785061da546Spatrick     return rnb_success;
3786061da546Spatrick   } else if (strstr(p, "vAttach") == p) {
3787061da546Spatrick     nub_process_t attach_pid =
3788061da546Spatrick         INVALID_NUB_PROCESS; // attach_pid will be set to 0 if the attach fails
3789061da546Spatrick     nub_process_t pid_attaching_to =
3790061da546Spatrick         INVALID_NUB_PROCESS; // pid_attaching_to is the original pid specified
3791061da546Spatrick     char err_str[1024] = {'\0'};
3792061da546Spatrick     std::string attach_name;
3793061da546Spatrick 
3794061da546Spatrick     if (strstr(p, "vAttachWait;") == p) {
3795061da546Spatrick       p += strlen("vAttachWait;");
3796061da546Spatrick       if (!GetProcessNameFrom_vAttach(p, attach_name)) {
3797061da546Spatrick         return HandlePacket_ILLFORMED(
3798061da546Spatrick             __FILE__, __LINE__, p, "non-hex char in arg on 'vAttachWait' pkt");
3799061da546Spatrick       }
3800be691f3bSpatrick       DNBLog("[LaunchAttach] START %d vAttachWait for process name '%s'",
3801be691f3bSpatrick              getpid(), attach_name.c_str());
3802061da546Spatrick       const bool ignore_existing = true;
3803061da546Spatrick       attach_pid = DNBProcessAttachWait(
3804be691f3bSpatrick           &m_ctx, attach_name.c_str(), ignore_existing, NULL, 1000, err_str,
3805be691f3bSpatrick           sizeof(err_str), RNBRemoteShouldCancelCallback);
3806061da546Spatrick 
3807061da546Spatrick     } else if (strstr(p, "vAttachOrWait;") == p) {
3808061da546Spatrick       p += strlen("vAttachOrWait;");
3809061da546Spatrick       if (!GetProcessNameFrom_vAttach(p, attach_name)) {
3810061da546Spatrick         return HandlePacket_ILLFORMED(
3811061da546Spatrick             __FILE__, __LINE__, p,
3812061da546Spatrick             "non-hex char in arg on 'vAttachOrWait' pkt");
3813061da546Spatrick       }
3814061da546Spatrick       const bool ignore_existing = false;
3815be691f3bSpatrick       DNBLog("[LaunchAttach] START %d vAttachWaitOrWait for process name "
3816be691f3bSpatrick              "'%s'",
3817be691f3bSpatrick              getpid(), attach_name.c_str());
3818061da546Spatrick       attach_pid = DNBProcessAttachWait(
3819be691f3bSpatrick           &m_ctx, attach_name.c_str(), ignore_existing, NULL, 1000, err_str,
3820be691f3bSpatrick           sizeof(err_str), RNBRemoteShouldCancelCallback);
3821061da546Spatrick     } else if (strstr(p, "vAttachName;") == p) {
3822061da546Spatrick       p += strlen("vAttachName;");
3823061da546Spatrick       if (!GetProcessNameFrom_vAttach(p, attach_name)) {
3824061da546Spatrick         return HandlePacket_ILLFORMED(
3825061da546Spatrick             __FILE__, __LINE__, p, "non-hex char in arg on 'vAttachName' pkt");
3826061da546Spatrick       }
3827061da546Spatrick 
3828be691f3bSpatrick       DNBLog("[LaunchAttach] START %d vAttachName attach to process name "
3829be691f3bSpatrick              "'%s'",
3830be691f3bSpatrick              getpid(), attach_name.c_str());
3831be691f3bSpatrick       attach_pid = DNBProcessAttachByName(attach_name.c_str(), NULL,
3832*f6aab3d8Srobert                                           Context().GetIgnoredExceptions(),
3833*f6aab3d8Srobert                                           err_str, sizeof(err_str));
3834061da546Spatrick 
3835061da546Spatrick     } else if (strstr(p, "vAttach;") == p) {
3836061da546Spatrick       p += strlen("vAttach;");
3837061da546Spatrick       char *end = NULL;
3838061da546Spatrick       pid_attaching_to = static_cast<int>(
3839061da546Spatrick           strtoul(p, &end, 16)); // PID will be in hex, so use base 16 to decode
3840061da546Spatrick       if (p != end && *end == '\0') {
3841061da546Spatrick         // Wait at most 30 second for attach
3842061da546Spatrick         struct timespec attach_timeout_abstime;
3843061da546Spatrick         DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, 30, 0);
3844be691f3bSpatrick         DNBLog("[LaunchAttach] START %d vAttach to pid %d", getpid(),
3845be691f3bSpatrick                pid_attaching_to);
3846061da546Spatrick         attach_pid = DNBProcessAttach(pid_attaching_to, &attach_timeout_abstime,
3847*f6aab3d8Srobert                                       m_ctx.GetIgnoredExceptions(),
3848*f6aab3d8Srobert                                       err_str, sizeof(err_str));
3849061da546Spatrick       }
3850061da546Spatrick     } else {
3851061da546Spatrick       return HandlePacket_UNIMPLEMENTED(p);
3852061da546Spatrick     }
3853061da546Spatrick 
3854*f6aab3d8Srobert     if (attach_pid == INVALID_NUB_PROCESS_ARCH) {
3855*f6aab3d8Srobert       DNBLogError("debugserver is x86_64 binary running in translation, attach "
3856*f6aab3d8Srobert                   "failed.");
3857*f6aab3d8Srobert       std::string return_message = "E96;";
3858*f6aab3d8Srobert       return_message +=
3859*f6aab3d8Srobert           cstring_to_asciihex_string("debugserver is x86_64 binary running in "
3860*f6aab3d8Srobert                                      "translation, attach failed.");
3861*f6aab3d8Srobert       SendPacket(return_message.c_str());
3862*f6aab3d8Srobert       return rnb_err;
3863*f6aab3d8Srobert     }
3864*f6aab3d8Srobert 
3865061da546Spatrick     if (attach_pid != INVALID_NUB_PROCESS) {
3866061da546Spatrick       if (m_ctx.ProcessID() != attach_pid)
3867061da546Spatrick         m_ctx.SetProcessID(attach_pid);
3868be691f3bSpatrick       DNBLog("Successfully attached to pid %d", attach_pid);
3869061da546Spatrick       // Send a stop reply packet to indicate we successfully attached!
3870061da546Spatrick       NotifyThatProcessStopped();
3871061da546Spatrick       return rnb_success;
3872061da546Spatrick     } else {
3873be691f3bSpatrick       DNBLogError("Attach failed");
3874061da546Spatrick       m_ctx.LaunchStatus().SetError(-1, DNBError::Generic);
3875061da546Spatrick       if (err_str[0])
3876061da546Spatrick         m_ctx.LaunchStatus().SetErrorString(err_str);
3877061da546Spatrick       else
3878061da546Spatrick         m_ctx.LaunchStatus().SetErrorString("attach failed");
3879061da546Spatrick 
3880061da546Spatrick       if (pid_attaching_to == INVALID_NUB_PROCESS && !attach_name.empty()) {
3881061da546Spatrick         pid_attaching_to = DNBProcessGetPIDByName(attach_name.c_str());
3882061da546Spatrick       }
3883061da546Spatrick 
3884dda28197Spatrick       // attach_pid is INVALID_NUB_PROCESS - we did not succeed in attaching
3885dda28197Spatrick       // if the original request, pid_attaching_to, is available, see if we
3886dda28197Spatrick       // can figure out why we couldn't attach.  Return an informative error
3887dda28197Spatrick       // string to lldb.
3888061da546Spatrick 
3889dda28197Spatrick       if (pid_attaching_to != INVALID_NUB_PROCESS) {
3890dda28197Spatrick         // The order of these checks is important.
3891dda28197Spatrick         if (process_does_not_exist (pid_attaching_to)) {
3892dda28197Spatrick           DNBLogError("Tried to attach to pid that doesn't exist");
3893061da546Spatrick           std::string return_message = "E96;";
3894dda28197Spatrick           return_message += cstring_to_asciihex_string("no such process.");
3895*f6aab3d8Srobert           return SendPacket(return_message);
3896061da546Spatrick         }
3897dda28197Spatrick         if (process_is_already_being_debugged (pid_attaching_to)) {
3898dda28197Spatrick           DNBLogError("Tried to attach to process already being debugged");
3899dda28197Spatrick           std::string return_message = "E96;";
3900dda28197Spatrick           return_message += cstring_to_asciihex_string("tried to attach to "
3901dda28197Spatrick                                            "process already being debugged");
3902*f6aab3d8Srobert           return SendPacket(return_message);
3903dda28197Spatrick         }
3904dda28197Spatrick         uid_t my_uid, process_uid;
3905dda28197Spatrick         if (attach_failed_due_to_uid_mismatch (pid_attaching_to,
3906dda28197Spatrick                                                my_uid, process_uid)) {
3907dda28197Spatrick           std::string my_username = "uid " + std::to_string (my_uid);
3908dda28197Spatrick           std::string process_username = "uid " + std::to_string (process_uid);
3909dda28197Spatrick           struct passwd *pw = getpwuid (my_uid);
3910dda28197Spatrick           if (pw && pw->pw_name) {
3911dda28197Spatrick             my_username = pw->pw_name;
3912dda28197Spatrick           }
3913dda28197Spatrick           pw = getpwuid (process_uid);
3914dda28197Spatrick           if (pw && pw->pw_name) {
3915dda28197Spatrick             process_username = pw->pw_name;
3916dda28197Spatrick           }
3917dda28197Spatrick           DNBLogError("Tried to attach to process with uid mismatch");
3918dda28197Spatrick           std::string return_message = "E96;";
3919dda28197Spatrick           std::string msg = "tried to attach to process as user '"
3920dda28197Spatrick                             + my_username + "' and process is running "
3921dda28197Spatrick                             "as user '" + process_username + "'";
3922dda28197Spatrick           return_message += cstring_to_asciihex_string(msg.c_str());
3923*f6aab3d8Srobert           return SendPacket(return_message);
3924dda28197Spatrick         }
3925dda28197Spatrick         if (!login_session_has_gui_access() && !developer_mode_enabled()) {
3926dda28197Spatrick           DNBLogError("Developer mode is not enabled and this is a "
3927dda28197Spatrick                       "non-interactive session");
3928dda28197Spatrick           std::string return_message = "E96;";
3929dda28197Spatrick           return_message += cstring_to_asciihex_string("developer mode is "
3930dda28197Spatrick                                            "not enabled on this machine "
3931dda28197Spatrick                                            "and this is a non-interactive "
3932dda28197Spatrick                                            "debug session.");
3933*f6aab3d8Srobert           return SendPacket(return_message);
3934dda28197Spatrick         }
3935dda28197Spatrick         if (!login_session_has_gui_access()) {
3936dda28197Spatrick           DNBLogError("This is a non-interactive session");
3937dda28197Spatrick           std::string return_message = "E96;";
3938dda28197Spatrick           return_message += cstring_to_asciihex_string("this is a "
3939dda28197Spatrick                                            "non-interactive debug session, "
3940dda28197Spatrick                                            "cannot get permission to debug "
3941dda28197Spatrick                                            "processes.");
3942*f6aab3d8Srobert           return SendPacket(return_message);
3943061da546Spatrick         }
3944061da546Spatrick       }
3945061da546Spatrick 
3946dda28197Spatrick       std::string error_explainer = "attach failed";
3947dda28197Spatrick       if (err_str[0] != '\0') {
3948dda28197Spatrick         // This is not a super helpful message for end users
3949dda28197Spatrick         if (strcmp (err_str, "unable to start the exception thread") == 0) {
3950dda28197Spatrick           snprintf (err_str, sizeof (err_str) - 1,
3951dda28197Spatrick                     "Not allowed to attach to process.  Look in the console "
3952be691f3bSpatrick                     "messages (Console.app), near the debugserver entries, "
3953be691f3bSpatrick                     "when the attach failed.  The subsystem that denied "
3954dda28197Spatrick                     "the attach permission will likely have logged an "
3955dda28197Spatrick                     "informative message about why it was denied.");
3956dda28197Spatrick           err_str[sizeof (err_str) - 1] = '\0';
3957dda28197Spatrick         }
3958dda28197Spatrick         error_explainer += " (";
3959dda28197Spatrick         error_explainer += err_str;
3960dda28197Spatrick         error_explainer += ")";
3961dda28197Spatrick       }
3962dda28197Spatrick       std::string default_return_msg = "E96;";
3963dda28197Spatrick       default_return_msg += cstring_to_asciihex_string
3964dda28197Spatrick                               (error_explainer.c_str());
3965*f6aab3d8Srobert       SendPacket (default_return_msg);
3966061da546Spatrick       DNBLogError("Attach failed: \"%s\".", err_str);
3967061da546Spatrick       return rnb_err;
3968061da546Spatrick     }
3969061da546Spatrick   }
3970061da546Spatrick 
3971061da546Spatrick   // All other failures come through here
3972061da546Spatrick   return HandlePacket_UNIMPLEMENTED(p);
3973061da546Spatrick }
3974061da546Spatrick 
3975061da546Spatrick /* 'T XX' -- status of thread
3976061da546Spatrick  Check if the specified thread is alive.
3977061da546Spatrick  The thread number is in hex?  */
3978061da546Spatrick 
HandlePacket_T(const char * p)3979061da546Spatrick rnb_err_t RNBRemote::HandlePacket_T(const char *p) {
3980061da546Spatrick   p++;
3981061da546Spatrick   if (p == NULL || *p == '\0') {
3982061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3983061da546Spatrick                                   "No thread specified in T packet");
3984061da546Spatrick   }
3985061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
3986061da546Spatrick     return SendPacket("E15");
3987061da546Spatrick   }
3988061da546Spatrick   errno = 0;
3989061da546Spatrick   nub_thread_t tid = strtoul(p, NULL, 16);
3990061da546Spatrick   if (errno != 0 && tid == 0) {
3991061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
3992061da546Spatrick                                   "Could not parse thread number in T packet");
3993061da546Spatrick   }
3994061da546Spatrick 
3995061da546Spatrick   nub_state_t state = DNBThreadGetState(m_ctx.ProcessID(), tid);
3996061da546Spatrick   if (state == eStateInvalid || state == eStateExited ||
3997061da546Spatrick       state == eStateCrashed) {
3998061da546Spatrick     return SendPacket("E16");
3999061da546Spatrick   }
4000061da546Spatrick 
4001061da546Spatrick   return SendPacket("OK");
4002061da546Spatrick }
4003061da546Spatrick 
HandlePacket_z(const char * p)4004061da546Spatrick rnb_err_t RNBRemote::HandlePacket_z(const char *p) {
4005061da546Spatrick   if (p == NULL || *p == '\0')
4006061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4007061da546Spatrick                                   "No thread specified in z packet");
4008061da546Spatrick 
4009061da546Spatrick   if (!m_ctx.HasValidProcessID())
4010061da546Spatrick     return SendPacket("E15");
4011061da546Spatrick 
4012061da546Spatrick   char packet_cmd = *p++;
4013061da546Spatrick   char break_type = *p++;
4014061da546Spatrick 
4015061da546Spatrick   if (*p++ != ',')
4016061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4017061da546Spatrick                                   "Comma separator missing in z packet");
4018061da546Spatrick 
4019061da546Spatrick   char *c = NULL;
4020061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
4021061da546Spatrick   errno = 0;
4022061da546Spatrick   nub_addr_t addr = strtoull(p, &c, 16);
4023061da546Spatrick   if (errno != 0 && addr == 0)
4024061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4025061da546Spatrick                                   "Invalid address in z packet");
4026061da546Spatrick   p = c;
4027061da546Spatrick   if (*p++ != ',')
4028061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4029061da546Spatrick                                   "Comma separator missing in z packet");
4030061da546Spatrick 
4031061da546Spatrick   errno = 0;
4032061da546Spatrick   auto byte_size = strtoul(p, &c, 16);
4033061da546Spatrick   if (errno != 0 && byte_size == 0)
4034061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4035061da546Spatrick                                   "Invalid length in z packet");
4036061da546Spatrick 
4037061da546Spatrick   if (packet_cmd == 'Z') {
4038061da546Spatrick     // set
4039061da546Spatrick     switch (break_type) {
4040061da546Spatrick     case '0': // set software breakpoint
4041061da546Spatrick     case '1': // set hardware breakpoint
4042061da546Spatrick     {
4043061da546Spatrick       // gdb can send multiple Z packets for the same address and
4044061da546Spatrick       // these calls must be ref counted.
4045061da546Spatrick       bool hardware = (break_type == '1');
4046061da546Spatrick 
4047061da546Spatrick       if (DNBBreakpointSet(pid, addr, byte_size, hardware)) {
4048061da546Spatrick         // We successfully created a breakpoint, now lets full out
4049061da546Spatrick         // a ref count structure with the breakID and add it to our
4050061da546Spatrick         // map.
4051061da546Spatrick         return SendPacket("OK");
4052061da546Spatrick       } else {
4053061da546Spatrick         // We failed to set the software breakpoint
4054061da546Spatrick         return SendPacket("E09");
4055061da546Spatrick       }
4056061da546Spatrick     } break;
4057061da546Spatrick 
4058061da546Spatrick     case '2': // set write watchpoint
4059061da546Spatrick     case '3': // set read watchpoint
4060061da546Spatrick     case '4': // set access watchpoint
4061061da546Spatrick     {
4062061da546Spatrick       bool hardware = true;
4063061da546Spatrick       uint32_t watch_flags = 0;
4064061da546Spatrick       if (break_type == '2')
4065061da546Spatrick         watch_flags = WATCH_TYPE_WRITE;
4066061da546Spatrick       else if (break_type == '3')
4067061da546Spatrick         watch_flags = WATCH_TYPE_READ;
4068061da546Spatrick       else
4069061da546Spatrick         watch_flags = WATCH_TYPE_READ | WATCH_TYPE_WRITE;
4070061da546Spatrick 
4071061da546Spatrick       if (DNBWatchpointSet(pid, addr, byte_size, watch_flags, hardware)) {
4072061da546Spatrick         return SendPacket("OK");
4073061da546Spatrick       } else {
4074061da546Spatrick         // We failed to set the watchpoint
4075061da546Spatrick         return SendPacket("E09");
4076061da546Spatrick       }
4077061da546Spatrick     } break;
4078061da546Spatrick 
4079061da546Spatrick     default:
4080061da546Spatrick       break;
4081061da546Spatrick     }
4082061da546Spatrick   } else if (packet_cmd == 'z') {
4083061da546Spatrick     // remove
4084061da546Spatrick     switch (break_type) {
4085061da546Spatrick     case '0': // remove software breakpoint
4086061da546Spatrick     case '1': // remove hardware breakpoint
4087061da546Spatrick       if (DNBBreakpointClear(pid, addr)) {
4088061da546Spatrick         return SendPacket("OK");
4089061da546Spatrick       } else {
4090061da546Spatrick         return SendPacket("E08");
4091061da546Spatrick       }
4092061da546Spatrick       break;
4093061da546Spatrick 
4094061da546Spatrick     case '2': // remove write watchpoint
4095061da546Spatrick     case '3': // remove read watchpoint
4096061da546Spatrick     case '4': // remove access watchpoint
4097061da546Spatrick       if (DNBWatchpointClear(pid, addr)) {
4098061da546Spatrick         return SendPacket("OK");
4099061da546Spatrick       } else {
4100061da546Spatrick         return SendPacket("E08");
4101061da546Spatrick       }
4102061da546Spatrick       break;
4103061da546Spatrick 
4104061da546Spatrick     default:
4105061da546Spatrick       break;
4106061da546Spatrick     }
4107061da546Spatrick   }
4108061da546Spatrick   return HandlePacket_UNIMPLEMENTED(p);
4109061da546Spatrick }
4110061da546Spatrick 
4111061da546Spatrick // Extract the thread number from the thread suffix that might be appended to
4112061da546Spatrick // thread specific packets. This will only be enabled if
4113061da546Spatrick // m_thread_suffix_supported
4114061da546Spatrick // is true.
ExtractThreadIDFromThreadSuffix(const char * p)4115061da546Spatrick nub_thread_t RNBRemote::ExtractThreadIDFromThreadSuffix(const char *p) {
4116061da546Spatrick   if (m_thread_suffix_supported) {
4117061da546Spatrick     nub_thread_t tid = INVALID_NUB_THREAD;
4118061da546Spatrick     if (p) {
4119061da546Spatrick       const char *tid_cstr = strstr(p, "thread:");
4120061da546Spatrick       if (tid_cstr) {
4121061da546Spatrick         tid_cstr += strlen("thread:");
4122061da546Spatrick         tid = strtoul(tid_cstr, NULL, 16);
4123061da546Spatrick       }
4124061da546Spatrick     }
4125061da546Spatrick     return tid;
4126061da546Spatrick   }
4127061da546Spatrick   return GetCurrentThread();
4128061da546Spatrick }
4129061da546Spatrick 
4130061da546Spatrick /* 'p XX'
4131061da546Spatrick  print the contents of register X */
4132061da546Spatrick 
HandlePacket_p(const char * p)4133061da546Spatrick rnb_err_t RNBRemote::HandlePacket_p(const char *p) {
4134061da546Spatrick   if (g_num_reg_entries == 0)
4135061da546Spatrick     InitializeRegisters();
4136061da546Spatrick 
4137061da546Spatrick   if (p == NULL || *p == '\0') {
4138061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4139061da546Spatrick                                   "No thread specified in p packet");
4140061da546Spatrick   }
4141061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
4142061da546Spatrick     return SendPacket("E15");
4143061da546Spatrick   }
4144061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
4145061da546Spatrick   errno = 0;
4146061da546Spatrick   char *tid_cstr = NULL;
4147061da546Spatrick   uint32_t reg = static_cast<uint32_t>(strtoul(p + 1, &tid_cstr, 16));
4148061da546Spatrick   if (errno != 0 && reg == 0) {
4149061da546Spatrick     return HandlePacket_ILLFORMED(
4150061da546Spatrick         __FILE__, __LINE__, p, "Could not parse register number in p packet");
4151061da546Spatrick   }
4152061da546Spatrick 
4153061da546Spatrick   nub_thread_t tid = ExtractThreadIDFromThreadSuffix(tid_cstr);
4154061da546Spatrick   if (tid == INVALID_NUB_THREAD)
4155061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4156061da546Spatrick                                   "No thread specified in p packet");
4157061da546Spatrick 
4158061da546Spatrick   const register_map_entry_t *reg_entry;
4159061da546Spatrick 
4160061da546Spatrick   if (reg < g_num_reg_entries)
4161061da546Spatrick     reg_entry = &g_reg_entries[reg];
4162061da546Spatrick   else
4163061da546Spatrick     reg_entry = NULL;
4164061da546Spatrick 
4165061da546Spatrick   std::ostringstream ostrm;
4166061da546Spatrick   if (reg_entry == NULL) {
4167061da546Spatrick     DNBLogError(
4168061da546Spatrick         "RNBRemote::HandlePacket_p(%s): unknown register number %u requested\n",
4169061da546Spatrick         p, reg);
4170061da546Spatrick     ostrm << "00000000";
4171061da546Spatrick   } else if (reg_entry->nub_info.reg == (uint32_t)-1) {
4172061da546Spatrick     if (reg_entry->nub_info.size > 0) {
4173061da546Spatrick       std::basic_string<uint8_t> zeros(reg_entry->nub_info.size, '\0');
4174061da546Spatrick       append_hex_value(ostrm, zeros.data(), zeros.size(), false);
4175061da546Spatrick     }
4176061da546Spatrick   } else {
4177061da546Spatrick     register_value_in_hex_fixed_width(ostrm, pid, tid, reg_entry, NULL);
4178061da546Spatrick   }
4179061da546Spatrick   return SendPacket(ostrm.str());
4180061da546Spatrick }
4181061da546Spatrick 
4182061da546Spatrick /* 'Pnn=rrrrr'
4183061da546Spatrick  Set register number n to value r.
4184061da546Spatrick  n and r are hex strings.  */
4185061da546Spatrick 
HandlePacket_P(const char * p)4186061da546Spatrick rnb_err_t RNBRemote::HandlePacket_P(const char *p) {
4187061da546Spatrick   if (g_num_reg_entries == 0)
4188061da546Spatrick     InitializeRegisters();
4189061da546Spatrick 
4190061da546Spatrick   if (p == NULL || *p == '\0') {
4191061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Empty P packet");
4192061da546Spatrick   }
4193061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
4194061da546Spatrick     return SendPacket("E28");
4195061da546Spatrick   }
4196061da546Spatrick 
4197061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
4198061da546Spatrick 
4199061da546Spatrick   StdStringExtractor packet(p);
4200061da546Spatrick 
4201061da546Spatrick   const char cmd_char = packet.GetChar();
4202061da546Spatrick   // Register ID is always in big endian
4203061da546Spatrick   const uint32_t reg = packet.GetHexMaxU32(false, UINT32_MAX);
4204061da546Spatrick   const char equal_char = packet.GetChar();
4205061da546Spatrick 
4206061da546Spatrick   if (cmd_char != 'P')
4207061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4208061da546Spatrick                                   "Improperly formed P packet");
4209061da546Spatrick 
4210061da546Spatrick   if (reg == UINT32_MAX)
4211061da546Spatrick     return SendPacket("E29");
4212061da546Spatrick 
4213061da546Spatrick   if (equal_char != '=')
4214061da546Spatrick     return SendPacket("E30");
4215061da546Spatrick 
4216061da546Spatrick   const register_map_entry_t *reg_entry;
4217061da546Spatrick 
4218061da546Spatrick   if (reg >= g_num_reg_entries)
4219061da546Spatrick     return SendPacket("E47");
4220061da546Spatrick 
4221061da546Spatrick   reg_entry = &g_reg_entries[reg];
4222061da546Spatrick 
4223061da546Spatrick   if (reg_entry->nub_info.set == (uint32_t)-1 &&
4224061da546Spatrick       reg_entry->nub_info.reg == (uint32_t)-1) {
4225061da546Spatrick     DNBLogError(
4226061da546Spatrick         "RNBRemote::HandlePacket_P(%s): unknown register number %u requested\n",
4227061da546Spatrick         p, reg);
4228061da546Spatrick     return SendPacket("E48");
4229061da546Spatrick   }
4230061da546Spatrick 
4231061da546Spatrick   DNBRegisterValue reg_value;
4232061da546Spatrick   reg_value.info = reg_entry->nub_info;
4233061da546Spatrick   packet.GetHexBytes(reg_value.value.v_sint8, reg_entry->nub_info.size, 0xcc);
4234061da546Spatrick 
4235061da546Spatrick   nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
4236061da546Spatrick   if (tid == INVALID_NUB_THREAD)
4237061da546Spatrick     return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4238061da546Spatrick                                   "No thread specified in p packet");
4239061da546Spatrick 
4240061da546Spatrick   if (!DNBThreadSetRegisterValueByID(pid, tid, reg_entry->nub_info.set,
4241061da546Spatrick                                      reg_entry->nub_info.reg, &reg_value)) {
4242061da546Spatrick     return SendPacket("E32");
4243061da546Spatrick   }
4244061da546Spatrick   return SendPacket("OK");
4245061da546Spatrick }
4246061da546Spatrick 
4247061da546Spatrick /* 'c [addr]'
4248061da546Spatrick  Continue, optionally from a specified address. */
4249061da546Spatrick 
HandlePacket_c(const char * p)4250061da546Spatrick rnb_err_t RNBRemote::HandlePacket_c(const char *p) {
4251061da546Spatrick   const nub_process_t pid = m_ctx.ProcessID();
4252061da546Spatrick 
4253061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
4254061da546Spatrick     return SendPacket("E23");
4255061da546Spatrick 
4256061da546Spatrick   DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateRunning, 0,
4257061da546Spatrick                                   INVALID_NUB_ADDRESS};
4258061da546Spatrick 
4259061da546Spatrick   if (*(p + 1) != '\0') {
4260061da546Spatrick     action.tid = GetContinueThread();
4261061da546Spatrick     errno = 0;
4262061da546Spatrick     action.addr = strtoull(p + 1, NULL, 16);
4263061da546Spatrick     if (errno != 0 && action.addr == 0)
4264061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4265061da546Spatrick                                     "Could not parse address in c packet");
4266061da546Spatrick   }
4267061da546Spatrick 
4268061da546Spatrick   DNBThreadResumeActions thread_actions;
4269061da546Spatrick   thread_actions.Append(action);
4270061da546Spatrick   thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
4271061da546Spatrick   if (!DNBProcessResume(pid, thread_actions.GetFirst(),
4272061da546Spatrick                         thread_actions.GetSize()))
4273061da546Spatrick     return SendPacket("E25");
4274061da546Spatrick   // Don't send an "OK" packet; response is the stopped/exited message.
4275061da546Spatrick   return rnb_success;
4276061da546Spatrick }
4277061da546Spatrick 
HandlePacket_MemoryRegionInfo(const char * p)4278061da546Spatrick rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
4279061da546Spatrick   /* This packet will find memory attributes (e.g. readable, writable,
4280061da546Spatrick      executable, stack, jitted code)
4281061da546Spatrick      for the memory region containing a given address and return that
4282061da546Spatrick      information.
4283061da546Spatrick 
4284061da546Spatrick      Users of this packet must be prepared for three results:
4285061da546Spatrick 
4286061da546Spatrick          Region information is returned
4287061da546Spatrick          Region information is unavailable for this address because the address
4288061da546Spatrick      is in unmapped memory
4289061da546Spatrick          Region lookup cannot be performed on this platform or process is not
4290061da546Spatrick      yet launched
4291061da546Spatrick          This packet isn't implemented
4292061da546Spatrick 
4293061da546Spatrick      Examples of use:
4294061da546Spatrick         qMemoryRegionInfo:3a55140
4295061da546Spatrick         start:3a50000,size:100000,permissions:rwx
4296061da546Spatrick 
4297061da546Spatrick         qMemoryRegionInfo:0
4298061da546Spatrick         error:address in unmapped region
4299061da546Spatrick 
4300061da546Spatrick         qMemoryRegionInfo:3a551140   (on a different platform)
4301061da546Spatrick         error:region lookup cannot be performed
4302061da546Spatrick 
4303061da546Spatrick         qMemoryRegionInfo
4304061da546Spatrick         OK                   // this packet is implemented by the remote nub
4305061da546Spatrick   */
4306061da546Spatrick 
4307061da546Spatrick   p += sizeof("qMemoryRegionInfo") - 1;
4308061da546Spatrick   if (*p == '\0')
4309061da546Spatrick     return SendPacket("OK");
4310061da546Spatrick   if (*p++ != ':')
4311061da546Spatrick     return SendPacket("E67");
4312061da546Spatrick   if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X'))
4313061da546Spatrick     p += 2;
4314061da546Spatrick 
4315061da546Spatrick   errno = 0;
4316061da546Spatrick   uint64_t address = strtoul(p, NULL, 16);
4317061da546Spatrick   if (errno != 0 && address == 0) {
4318061da546Spatrick     return HandlePacket_ILLFORMED(
4319061da546Spatrick         __FILE__, __LINE__, p, "Invalid address in qMemoryRegionInfo packet");
4320061da546Spatrick   }
4321061da546Spatrick 
4322be691f3bSpatrick   DNBRegionInfo region_info;
4323061da546Spatrick   DNBProcessMemoryRegionInfo(m_ctx.ProcessID(), address, &region_info);
4324061da546Spatrick   std::ostringstream ostrm;
4325061da546Spatrick 
4326061da546Spatrick   // start:3a50000,size:100000,permissions:rwx
4327061da546Spatrick   ostrm << "start:" << std::hex << region_info.addr << ';';
4328061da546Spatrick 
4329061da546Spatrick   if (region_info.size > 0)
4330061da546Spatrick     ostrm << "size:" << std::hex << region_info.size << ';';
4331061da546Spatrick 
4332061da546Spatrick   if (region_info.permissions) {
4333061da546Spatrick     ostrm << "permissions:";
4334061da546Spatrick 
4335061da546Spatrick     if (region_info.permissions & eMemoryPermissionsReadable)
4336061da546Spatrick       ostrm << 'r';
4337061da546Spatrick     if (region_info.permissions & eMemoryPermissionsWritable)
4338061da546Spatrick       ostrm << 'w';
4339061da546Spatrick     if (region_info.permissions & eMemoryPermissionsExecutable)
4340061da546Spatrick       ostrm << 'x';
4341061da546Spatrick     ostrm << ';';
4342be691f3bSpatrick 
4343be691f3bSpatrick     ostrm << "dirty-pages:";
4344be691f3bSpatrick     if (region_info.dirty_pages.size() > 0) {
4345be691f3bSpatrick       bool first = true;
4346be691f3bSpatrick       for (nub_addr_t addr : region_info.dirty_pages) {
4347be691f3bSpatrick         if (!first)
4348be691f3bSpatrick           ostrm << ",";
4349be691f3bSpatrick         first = false;
4350*f6aab3d8Srobert         ostrm << std::hex << addr;
4351be691f3bSpatrick       }
4352be691f3bSpatrick     }
4353be691f3bSpatrick     ostrm << ";";
4354*f6aab3d8Srobert     if (!region_info.vm_types.empty()) {
4355*f6aab3d8Srobert       ostrm << "type:";
4356*f6aab3d8Srobert       for (size_t i = 0; i < region_info.vm_types.size(); i++) {
4357*f6aab3d8Srobert         if (i)
4358*f6aab3d8Srobert           ostrm << ",";
4359*f6aab3d8Srobert         ostrm << region_info.vm_types[i];
4360*f6aab3d8Srobert       }
4361*f6aab3d8Srobert       ostrm << ";";
4362*f6aab3d8Srobert     }
4363061da546Spatrick   }
4364061da546Spatrick   return SendPacket(ostrm.str());
4365061da546Spatrick }
4366061da546Spatrick 
4367061da546Spatrick // qGetProfileData;scan_type:0xYYYYYYY
HandlePacket_GetProfileData(const char * p)4368061da546Spatrick rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) {
4369061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
4370061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
4371061da546Spatrick     return SendPacket("OK");
4372061da546Spatrick 
4373061da546Spatrick   StdStringExtractor packet(p += sizeof("qGetProfileData"));
4374061da546Spatrick   DNBProfileDataScanType scan_type = eProfileAll;
4375061da546Spatrick   std::string name;
4376061da546Spatrick   std::string value;
4377061da546Spatrick   while (packet.GetNameColonValue(name, value)) {
4378061da546Spatrick     if (name == "scan_type") {
4379061da546Spatrick       std::istringstream iss(value);
4380061da546Spatrick       uint32_t int_value = 0;
4381061da546Spatrick       if (iss >> std::hex >> int_value) {
4382061da546Spatrick         scan_type = (DNBProfileDataScanType)int_value;
4383061da546Spatrick       }
4384061da546Spatrick     }
4385061da546Spatrick   }
4386061da546Spatrick 
4387061da546Spatrick   std::string data = DNBProcessGetProfileData(pid, scan_type);
4388061da546Spatrick   if (!data.empty()) {
4389*f6aab3d8Srobert     return SendPacket(data);
4390061da546Spatrick   } else {
4391061da546Spatrick     return SendPacket("OK");
4392061da546Spatrick   }
4393061da546Spatrick }
4394061da546Spatrick 
4395061da546Spatrick // QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;scan_type:0xYYYYYYY
HandlePacket_SetEnableAsyncProfiling(const char * p)4396061da546Spatrick rnb_err_t RNBRemote::HandlePacket_SetEnableAsyncProfiling(const char *p) {
4397061da546Spatrick   nub_process_t pid = m_ctx.ProcessID();
4398061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
4399061da546Spatrick     return SendPacket("OK");
4400061da546Spatrick 
4401061da546Spatrick   StdStringExtractor packet(p += sizeof("QSetEnableAsyncProfiling"));
4402061da546Spatrick   bool enable = false;
4403061da546Spatrick   uint64_t interval_usec = 0;
4404061da546Spatrick   DNBProfileDataScanType scan_type = eProfileAll;
4405061da546Spatrick   std::string name;
4406061da546Spatrick   std::string value;
4407061da546Spatrick   while (packet.GetNameColonValue(name, value)) {
4408061da546Spatrick     if (name == "enable") {
4409061da546Spatrick       enable = strtoul(value.c_str(), NULL, 10) > 0;
4410061da546Spatrick     } else if (name == "interval_usec") {
4411061da546Spatrick       interval_usec = strtoul(value.c_str(), NULL, 10);
4412061da546Spatrick     } else if (name == "scan_type") {
4413061da546Spatrick       std::istringstream iss(value);
4414061da546Spatrick       uint32_t int_value = 0;
4415061da546Spatrick       if (iss >> std::hex >> int_value) {
4416061da546Spatrick         scan_type = (DNBProfileDataScanType)int_value;
4417061da546Spatrick       }
4418061da546Spatrick     }
4419061da546Spatrick   }
4420061da546Spatrick 
4421061da546Spatrick   if (interval_usec == 0) {
4422061da546Spatrick     enable = false;
4423061da546Spatrick   }
4424061da546Spatrick 
4425061da546Spatrick   DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec, scan_type);
4426061da546Spatrick   return SendPacket("OK");
4427061da546Spatrick }
4428061da546Spatrick 
4429061da546Spatrick // QEnableCompression:type:<COMPRESSION-TYPE>;minsize:<MINIMUM PACKET SIZE TO
4430061da546Spatrick // COMPRESS>;
4431061da546Spatrick //
4432061da546Spatrick // type: must be a type previously reported by the qXfer:features:
4433061da546Spatrick // SupportedCompressions list
4434061da546Spatrick //
4435061da546Spatrick // minsize: is optional; by default the qXfer:features:
4436061da546Spatrick // DefaultCompressionMinSize value is used
4437061da546Spatrick // debugserver may have a better idea of what a good minimum packet size to
4438061da546Spatrick // compress is than lldb.
4439061da546Spatrick 
HandlePacket_QEnableCompression(const char * p)4440061da546Spatrick rnb_err_t RNBRemote::HandlePacket_QEnableCompression(const char *p) {
4441061da546Spatrick   p += sizeof("QEnableCompression:") - 1;
4442061da546Spatrick 
4443061da546Spatrick   size_t new_compression_minsize = m_compression_minsize;
4444061da546Spatrick   const char *new_compression_minsize_str = strstr(p, "minsize:");
4445061da546Spatrick   if (new_compression_minsize_str) {
4446061da546Spatrick     new_compression_minsize_str += strlen("minsize:");
4447061da546Spatrick     errno = 0;
4448061da546Spatrick     new_compression_minsize = strtoul(new_compression_minsize_str, NULL, 10);
4449061da546Spatrick     if (errno != 0 || new_compression_minsize == ULONG_MAX) {
4450061da546Spatrick       new_compression_minsize = m_compression_minsize;
4451061da546Spatrick     }
4452061da546Spatrick   }
4453061da546Spatrick 
4454061da546Spatrick   if (strstr(p, "type:zlib-deflate;") != nullptr) {
4455061da546Spatrick     EnableCompressionNextSendPacket(compression_types::zlib_deflate);
4456061da546Spatrick     m_compression_minsize = new_compression_minsize;
4457061da546Spatrick     return SendPacket("OK");
4458061da546Spatrick   } else if (strstr(p, "type:lz4;") != nullptr) {
4459061da546Spatrick     EnableCompressionNextSendPacket(compression_types::lz4);
4460061da546Spatrick     m_compression_minsize = new_compression_minsize;
4461061da546Spatrick     return SendPacket("OK");
4462061da546Spatrick   } else if (strstr(p, "type:lzma;") != nullptr) {
4463061da546Spatrick     EnableCompressionNextSendPacket(compression_types::lzma);
4464061da546Spatrick     m_compression_minsize = new_compression_minsize;
4465061da546Spatrick     return SendPacket("OK");
4466061da546Spatrick   } else if (strstr(p, "type:lzfse;") != nullptr) {
4467061da546Spatrick     EnableCompressionNextSendPacket(compression_types::lzfse);
4468061da546Spatrick     m_compression_minsize = new_compression_minsize;
4469061da546Spatrick     return SendPacket("OK");
4470061da546Spatrick   }
4471061da546Spatrick 
4472061da546Spatrick   return SendPacket("E88");
4473061da546Spatrick }
4474061da546Spatrick 
HandlePacket_qSpeedTest(const char * p)4475061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qSpeedTest(const char *p) {
4476061da546Spatrick   p += strlen("qSpeedTest:response_size:");
4477061da546Spatrick   char *end = NULL;
4478061da546Spatrick   errno = 0;
4479061da546Spatrick   uint64_t response_size = ::strtoul(p, &end, 16);
4480061da546Spatrick   if (errno != 0)
4481061da546Spatrick     return HandlePacket_ILLFORMED(
4482061da546Spatrick         __FILE__, __LINE__, p,
4483061da546Spatrick         "Didn't find response_size value at right offset");
4484061da546Spatrick   else if (*end == ';') {
4485be691f3bSpatrick     static char g_data[4 * 1024 * 1024 + 16];
4486be691f3bSpatrick     strcpy(g_data, "data:");
4487061da546Spatrick     memset(g_data + 5, 'a', response_size);
4488061da546Spatrick     g_data[response_size + 5] = '\0';
4489061da546Spatrick     return SendPacket(g_data);
4490061da546Spatrick   } else {
4491061da546Spatrick     return SendPacket("E79");
4492061da546Spatrick   }
4493061da546Spatrick }
4494061da546Spatrick 
HandlePacket_WatchpointSupportInfo(const char * p)4495061da546Spatrick rnb_err_t RNBRemote::HandlePacket_WatchpointSupportInfo(const char *p) {
4496061da546Spatrick   /* This packet simply returns the number of supported hardware watchpoints.
4497061da546Spatrick 
4498061da546Spatrick      Examples of use:
4499061da546Spatrick         qWatchpointSupportInfo:
4500061da546Spatrick         num:4
4501061da546Spatrick 
4502061da546Spatrick         qWatchpointSupportInfo
4503061da546Spatrick         OK                   // this packet is implemented by the remote nub
4504061da546Spatrick   */
4505061da546Spatrick 
4506061da546Spatrick   p += sizeof("qWatchpointSupportInfo") - 1;
4507061da546Spatrick   if (*p == '\0')
4508061da546Spatrick     return SendPacket("OK");
4509061da546Spatrick   if (*p++ != ':')
4510061da546Spatrick     return SendPacket("E67");
4511061da546Spatrick 
4512061da546Spatrick   errno = 0;
4513061da546Spatrick   uint32_t num = DNBWatchpointGetNumSupportedHWP(m_ctx.ProcessID());
4514061da546Spatrick   std::ostringstream ostrm;
4515061da546Spatrick 
4516061da546Spatrick   // size:4
4517061da546Spatrick   ostrm << "num:" << std::dec << num << ';';
4518061da546Spatrick   return SendPacket(ostrm.str());
4519061da546Spatrick }
4520061da546Spatrick 
4521061da546Spatrick /* 'C sig [;addr]'
4522061da546Spatrick  Resume with signal sig, optionally at address addr.  */
4523061da546Spatrick 
HandlePacket_C(const char * p)4524061da546Spatrick rnb_err_t RNBRemote::HandlePacket_C(const char *p) {
4525061da546Spatrick   const nub_process_t pid = m_ctx.ProcessID();
4526061da546Spatrick 
4527061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
4528061da546Spatrick     return SendPacket("E36");
4529061da546Spatrick 
4530061da546Spatrick   DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateRunning, 0,
4531061da546Spatrick                                   INVALID_NUB_ADDRESS};
4532061da546Spatrick   int process_signo = -1;
4533061da546Spatrick   if (*(p + 1) != '\0') {
4534061da546Spatrick     action.tid = GetContinueThread();
4535061da546Spatrick     char *end = NULL;
4536061da546Spatrick     errno = 0;
4537061da546Spatrick     process_signo = static_cast<int>(strtoul(p + 1, &end, 16));
4538061da546Spatrick     if (errno != 0)
4539061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4540061da546Spatrick                                     "Could not parse signal in C packet");
4541061da546Spatrick     else if (*end == ';') {
4542061da546Spatrick       errno = 0;
4543061da546Spatrick       action.addr = strtoull(end + 1, NULL, 16);
4544061da546Spatrick       if (errno != 0 && action.addr == 0)
4545061da546Spatrick         return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4546061da546Spatrick                                       "Could not parse address in C packet");
4547061da546Spatrick     }
4548061da546Spatrick   }
4549061da546Spatrick 
4550061da546Spatrick   DNBThreadResumeActions thread_actions;
4551061da546Spatrick   thread_actions.Append(action);
4552061da546Spatrick   thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, action.signal);
4553061da546Spatrick   if (!DNBProcessSignal(pid, process_signo))
4554061da546Spatrick     return SendPacket("E52");
4555061da546Spatrick   if (!DNBProcessResume(pid, thread_actions.GetFirst(),
4556061da546Spatrick                         thread_actions.GetSize()))
4557061da546Spatrick     return SendPacket("E38");
4558061da546Spatrick   /* Don't send an "OK" packet; response is the stopped/exited message.  */
4559061da546Spatrick   return rnb_success;
4560061da546Spatrick }
4561061da546Spatrick 
4562061da546Spatrick // 'D' packet
4563061da546Spatrick // Detach from gdb.
HandlePacket_D(const char * p)4564061da546Spatrick rnb_err_t RNBRemote::HandlePacket_D(const char *p) {
4565061da546Spatrick   if (m_ctx.HasValidProcessID()) {
4566be691f3bSpatrick     DNBLog("detaching from pid %u due to D packet", m_ctx.ProcessID());
4567061da546Spatrick     if (DNBProcessDetach(m_ctx.ProcessID()))
4568061da546Spatrick       SendPacket("OK");
4569be691f3bSpatrick     else {
4570be691f3bSpatrick       DNBLog("error while detaching from pid %u due to D packet",
4571be691f3bSpatrick              m_ctx.ProcessID());
4572061da546Spatrick       SendPacket("E");
4573be691f3bSpatrick     }
4574061da546Spatrick   } else {
4575061da546Spatrick     SendPacket("E");
4576061da546Spatrick   }
4577061da546Spatrick   return rnb_success;
4578061da546Spatrick }
4579061da546Spatrick 
4580061da546Spatrick /* 'k'
4581061da546Spatrick  Kill the inferior process.  */
4582061da546Spatrick 
HandlePacket_k(const char * p)4583061da546Spatrick rnb_err_t RNBRemote::HandlePacket_k(const char *p) {
4584061da546Spatrick   DNBLog("Got a 'k' packet, killing the inferior process.");
4585061da546Spatrick   // No response to should be sent to the kill packet
4586061da546Spatrick   if (m_ctx.HasValidProcessID())
4587061da546Spatrick     DNBProcessKill(m_ctx.ProcessID());
4588061da546Spatrick   SendPacket("X09");
4589061da546Spatrick   return rnb_success;
4590061da546Spatrick }
4591061da546Spatrick 
HandlePacket_stop_process(const char * p)4592061da546Spatrick rnb_err_t RNBRemote::HandlePacket_stop_process(const char *p) {
4593061da546Spatrick //#define TEST_EXIT_ON_INTERRUPT // This should only be uncommented to test
4594061da546Spatrick //exiting on interrupt
4595061da546Spatrick #if defined(TEST_EXIT_ON_INTERRUPT)
4596061da546Spatrick   rnb_err_t err = HandlePacket_k(p);
4597061da546Spatrick   m_comm.Disconnect(true);
4598061da546Spatrick   return err;
4599061da546Spatrick #else
4600061da546Spatrick   if (!DNBProcessInterrupt(m_ctx.ProcessID())) {
4601061da546Spatrick     // If we failed to interrupt the process, then send a stop
4602061da546Spatrick     // reply packet as the process was probably already stopped
4603061da546Spatrick     DNBLogThreaded("RNBRemote::HandlePacket_stop_process() sending extra stop "
4604061da546Spatrick                    "reply because DNBProcessInterrupt returned false");
4605061da546Spatrick     HandlePacket_last_signal(NULL);
4606061da546Spatrick   }
4607061da546Spatrick   return rnb_success;
4608061da546Spatrick #endif
4609061da546Spatrick }
4610061da546Spatrick 
4611061da546Spatrick /* 's'
4612061da546Spatrick  Step the inferior process.  */
4613061da546Spatrick 
HandlePacket_s(const char * p)4614061da546Spatrick rnb_err_t RNBRemote::HandlePacket_s(const char *p) {
4615061da546Spatrick   const nub_process_t pid = m_ctx.ProcessID();
4616061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
4617061da546Spatrick     return SendPacket("E32");
4618061da546Spatrick 
4619061da546Spatrick   // Hardware supported stepping not supported on arm
4620061da546Spatrick   nub_thread_t tid = GetContinueThread();
4621061da546Spatrick   if (tid == 0 || tid == (nub_thread_t)-1)
4622061da546Spatrick     tid = GetCurrentThread();
4623061da546Spatrick 
4624061da546Spatrick   if (tid == INVALID_NUB_THREAD)
4625061da546Spatrick     return SendPacket("E33");
4626061da546Spatrick 
4627061da546Spatrick   DNBThreadResumeActions thread_actions;
4628061da546Spatrick   thread_actions.AppendAction(tid, eStateStepping);
4629061da546Spatrick 
4630061da546Spatrick   // Make all other threads stop when we are stepping
4631061da546Spatrick   thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
4632061da546Spatrick   if (!DNBProcessResume(pid, thread_actions.GetFirst(),
4633061da546Spatrick                         thread_actions.GetSize()))
4634061da546Spatrick     return SendPacket("E49");
4635061da546Spatrick   // Don't send an "OK" packet; response is the stopped/exited message.
4636061da546Spatrick   return rnb_success;
4637061da546Spatrick }
4638061da546Spatrick 
4639061da546Spatrick /* 'S sig [;addr]'
4640061da546Spatrick  Step with signal sig, optionally at address addr.  */
4641061da546Spatrick 
HandlePacket_S(const char * p)4642061da546Spatrick rnb_err_t RNBRemote::HandlePacket_S(const char *p) {
4643061da546Spatrick   const nub_process_t pid = m_ctx.ProcessID();
4644061da546Spatrick   if (pid == INVALID_NUB_PROCESS)
4645061da546Spatrick     return SendPacket("E36");
4646061da546Spatrick 
4647061da546Spatrick   DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateStepping, 0,
4648061da546Spatrick                                   INVALID_NUB_ADDRESS};
4649061da546Spatrick 
4650061da546Spatrick   if (*(p + 1) != '\0') {
4651061da546Spatrick     char *end = NULL;
4652061da546Spatrick     errno = 0;
4653061da546Spatrick     action.signal = static_cast<int>(strtoul(p + 1, &end, 16));
4654061da546Spatrick     if (errno != 0)
4655061da546Spatrick       return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4656061da546Spatrick                                     "Could not parse signal in S packet");
4657061da546Spatrick     else if (*end == ';') {
4658061da546Spatrick       errno = 0;
4659061da546Spatrick       action.addr = strtoull(end + 1, NULL, 16);
4660061da546Spatrick       if (errno != 0 && action.addr == 0) {
4661061da546Spatrick         return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
4662061da546Spatrick                                       "Could not parse address in S packet");
4663061da546Spatrick       }
4664061da546Spatrick     }
4665061da546Spatrick   }
4666061da546Spatrick 
4667061da546Spatrick   action.tid = GetContinueThread();
4668061da546Spatrick   if (action.tid == 0 || action.tid == (nub_thread_t)-1)
4669061da546Spatrick     return SendPacket("E40");
4670061da546Spatrick 
4671061da546Spatrick   nub_state_t tstate = DNBThreadGetState(pid, action.tid);
4672061da546Spatrick   if (tstate == eStateInvalid || tstate == eStateExited)
4673061da546Spatrick     return SendPacket("E37");
4674061da546Spatrick 
4675061da546Spatrick   DNBThreadResumeActions thread_actions;
4676061da546Spatrick   thread_actions.Append(action);
4677061da546Spatrick 
4678061da546Spatrick   // Make all other threads stop when we are stepping
4679061da546Spatrick   thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
4680061da546Spatrick   if (!DNBProcessResume(pid, thread_actions.GetFirst(),
4681061da546Spatrick                         thread_actions.GetSize()))
4682061da546Spatrick     return SendPacket("E39");
4683061da546Spatrick 
4684061da546Spatrick   // Don't send an "OK" packet; response is the stopped/exited message.
4685061da546Spatrick   return rnb_success;
4686061da546Spatrick }
4687061da546Spatrick 
GetArchName(const uint32_t cputype,const uint32_t cpusubtype)4688061da546Spatrick static const char *GetArchName(const uint32_t cputype,
4689061da546Spatrick                                const uint32_t cpusubtype) {
4690061da546Spatrick   switch (cputype) {
4691061da546Spatrick   case CPU_TYPE_ARM:
4692061da546Spatrick     switch (cpusubtype) {
4693061da546Spatrick     case 5:
4694061da546Spatrick       return "armv4";
4695061da546Spatrick     case 6:
4696061da546Spatrick       return "armv6";
4697061da546Spatrick     case 7:
4698061da546Spatrick       return "armv5t";
4699061da546Spatrick     case 8:
4700061da546Spatrick       return "xscale";
4701061da546Spatrick     case 9:
4702061da546Spatrick       return "armv7";
4703061da546Spatrick     case 10:
4704061da546Spatrick       return "armv7f";
4705061da546Spatrick     case 11:
4706061da546Spatrick       return "armv7s";
4707061da546Spatrick     case 12:
4708061da546Spatrick       return "armv7k";
4709061da546Spatrick     case 14:
4710061da546Spatrick       return "armv6m";
4711061da546Spatrick     case 15:
4712061da546Spatrick       return "armv7m";
4713061da546Spatrick     case 16:
4714061da546Spatrick       return "armv7em";
4715061da546Spatrick     default:
4716061da546Spatrick       return "arm";
4717061da546Spatrick     }
4718061da546Spatrick     break;
4719061da546Spatrick   case CPU_TYPE_ARM64:
4720061da546Spatrick     return "arm64";
4721061da546Spatrick   case CPU_TYPE_ARM64_32:
4722061da546Spatrick     return "arm64_32";
4723061da546Spatrick   case CPU_TYPE_I386:
4724061da546Spatrick     return "i386";
4725061da546Spatrick   case CPU_TYPE_X86_64:
4726061da546Spatrick     switch (cpusubtype) {
4727061da546Spatrick     default:
4728061da546Spatrick       return "x86_64";
4729061da546Spatrick     case 8:
4730061da546Spatrick       return "x86_64h";
4731061da546Spatrick     }
4732061da546Spatrick     break;
4733061da546Spatrick   }
4734061da546Spatrick   return NULL;
4735061da546Spatrick }
4736061da546Spatrick 
GetHostCPUType(uint32_t & cputype,uint32_t & cpusubtype,uint32_t & is_64_bit_capable,bool & promoted_to_64)4737061da546Spatrick static bool GetHostCPUType(uint32_t &cputype, uint32_t &cpusubtype,
4738061da546Spatrick                            uint32_t &is_64_bit_capable, bool &promoted_to_64) {
4739061da546Spatrick   static uint32_t g_host_cputype = 0;
4740061da546Spatrick   static uint32_t g_host_cpusubtype = 0;
4741061da546Spatrick   static uint32_t g_is_64_bit_capable = 0;
4742061da546Spatrick   static bool g_promoted_to_64 = false;
4743061da546Spatrick 
4744061da546Spatrick   if (g_host_cputype == 0) {
4745061da546Spatrick     g_promoted_to_64 = false;
4746061da546Spatrick     size_t len = sizeof(uint32_t);
4747061da546Spatrick     if (::sysctlbyname("hw.cputype", &g_host_cputype, &len, NULL, 0) == 0) {
4748061da546Spatrick       len = sizeof(uint32_t);
4749061da546Spatrick       if (::sysctlbyname("hw.cpu64bit_capable", &g_is_64_bit_capable, &len,
4750061da546Spatrick                          NULL, 0) == 0) {
4751061da546Spatrick         if (g_is_64_bit_capable && ((g_host_cputype & CPU_ARCH_ABI64) == 0)) {
4752061da546Spatrick           g_promoted_to_64 = true;
4753061da546Spatrick           g_host_cputype |= CPU_ARCH_ABI64;
4754061da546Spatrick         }
4755061da546Spatrick       }
4756061da546Spatrick #if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
4757061da546Spatrick       if (g_host_cputype == CPU_TYPE_ARM64 && sizeof (void*) == 4)
4758061da546Spatrick         g_host_cputype = CPU_TYPE_ARM64_32;
4759061da546Spatrick #endif
4760061da546Spatrick     }
4761061da546Spatrick 
4762061da546Spatrick     len = sizeof(uint32_t);
4763061da546Spatrick     if (::sysctlbyname("hw.cpusubtype", &g_host_cpusubtype, &len, NULL, 0) ==
4764061da546Spatrick         0) {
4765061da546Spatrick       if (g_promoted_to_64 && g_host_cputype == CPU_TYPE_X86_64 &&
4766061da546Spatrick           g_host_cpusubtype == CPU_SUBTYPE_486)
4767061da546Spatrick         g_host_cpusubtype = CPU_SUBTYPE_X86_64_ALL;
4768061da546Spatrick     }
4769061da546Spatrick #if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
4770061da546Spatrick     // on arm64_32 devices, the machine's native cpu type is
4771061da546Spatrick     // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e.
4772061da546Spatrick     // But we change the cputype to CPU_TYPE_ARM64_32 because
4773061da546Spatrick     // the user processes are all ILP32 processes today.
4774061da546Spatrick     // We also need to rewrite the cpusubtype so we vend
4775061da546Spatrick     // a valid cputype + cpusubtype combination.
4776061da546Spatrick     if (g_host_cputype == CPU_TYPE_ARM64_32)
4777061da546Spatrick       g_host_cpusubtype = CPU_SUBTYPE_ARM64_32_V8;
4778061da546Spatrick #endif
4779061da546Spatrick   }
4780061da546Spatrick 
4781061da546Spatrick   cputype = g_host_cputype;
4782061da546Spatrick   cpusubtype = g_host_cpusubtype;
4783061da546Spatrick   is_64_bit_capable = g_is_64_bit_capable;
4784061da546Spatrick   promoted_to_64 = g_promoted_to_64;
4785061da546Spatrick   return g_host_cputype != 0;
4786061da546Spatrick }
4787061da546Spatrick 
HandlePacket_qHostInfo(const char * p)4788061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) {
4789061da546Spatrick   std::ostringstream strm;
4790061da546Spatrick 
4791061da546Spatrick   uint32_t cputype = 0;
4792061da546Spatrick   uint32_t cpusubtype = 0;
4793061da546Spatrick   uint32_t is_64_bit_capable = 0;
4794061da546Spatrick   bool promoted_to_64 = false;
4795061da546Spatrick   if (GetHostCPUType(cputype, cpusubtype, is_64_bit_capable, promoted_to_64)) {
4796061da546Spatrick     strm << "cputype:" << std::dec << cputype << ';';
4797061da546Spatrick     strm << "cpusubtype:" << std::dec << cpusubtype << ';';
4798061da546Spatrick   }
4799061da546Spatrick 
4800061da546Spatrick   uint32_t addressing_bits = 0;
4801*f6aab3d8Srobert   if (DNBGetAddressingBits(addressing_bits)) {
4802061da546Spatrick     strm << "addressing_bits:" << std::dec << addressing_bits << ';';
4803061da546Spatrick   }
4804061da546Spatrick 
4805061da546Spatrick   // The OS in the triple should be "ios" or "macosx" which doesn't match our
4806061da546Spatrick   // "Darwin" which gets returned from "kern.ostype", so we need to hardcode
4807061da546Spatrick   // this for now.
4808061da546Spatrick   if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64
4809061da546Spatrick       || cputype == CPU_TYPE_ARM64_32) {
4810061da546Spatrick #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
4811061da546Spatrick     strm << "ostype:tvos;";
4812061da546Spatrick #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
4813061da546Spatrick     strm << "ostype:watchos;";
4814061da546Spatrick #elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
4815061da546Spatrick     strm << "ostype:bridgeos;";
4816dda28197Spatrick #elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1
4817dda28197Spatrick     strm << "ostype:macosx;";
4818061da546Spatrick #else
4819061da546Spatrick     strm << "ostype:ios;";
4820061da546Spatrick #endif
4821061da546Spatrick 
4822061da546Spatrick     // On armv7 we use "synchronous" watchpoints which means the exception is
4823061da546Spatrick     // delivered before the instruction executes.
4824061da546Spatrick     strm << "watchpoint_exceptions_received:before;";
4825061da546Spatrick   } else {
4826061da546Spatrick     strm << "ostype:macosx;";
4827061da546Spatrick     strm << "watchpoint_exceptions_received:after;";
4828061da546Spatrick   }
4829061da546Spatrick   //    char ostype[64];
4830061da546Spatrick   //    len = sizeof(ostype);
4831061da546Spatrick   //    if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0)
4832061da546Spatrick   //    {
4833061da546Spatrick   //        len = strlen(ostype);
4834061da546Spatrick   //        std::transform (ostype, ostype + len, ostype, tolower);
4835061da546Spatrick   //        strm << "ostype:" << std::dec << ostype << ';';
4836061da546Spatrick   //    }
4837061da546Spatrick 
4838061da546Spatrick   strm << "vendor:apple;";
4839061da546Spatrick 
4840061da546Spatrick   uint64_t major, minor, patch;
4841061da546Spatrick   if (DNBGetOSVersionNumbers(&major, &minor, &patch)) {
4842061da546Spatrick     strm << "os_version:" << major << "." << minor;
4843061da546Spatrick     if (patch != UINT64_MAX)
4844061da546Spatrick       strm << "." << patch;
4845061da546Spatrick     strm << ";";
4846061da546Spatrick   }
4847061da546Spatrick 
4848061da546Spatrick   std::string maccatalyst_version = DNBGetMacCatalystVersionString();
4849061da546Spatrick   if (!maccatalyst_version.empty() &&
4850061da546Spatrick       std::all_of(maccatalyst_version.begin(), maccatalyst_version.end(),
4851061da546Spatrick                   [](char c) { return (c >= '0' && c <= '9') || c == '.'; }))
4852061da546Spatrick     strm << "maccatalyst_version:" << maccatalyst_version << ";";
4853061da546Spatrick 
4854061da546Spatrick #if defined(__LITTLE_ENDIAN__)
4855061da546Spatrick   strm << "endian:little;";
4856061da546Spatrick #elif defined(__BIG_ENDIAN__)
4857061da546Spatrick   strm << "endian:big;";
4858061da546Spatrick #elif defined(__PDP_ENDIAN__)
4859061da546Spatrick   strm << "endian:pdp;";
4860061da546Spatrick #endif
4861061da546Spatrick 
4862061da546Spatrick   if (promoted_to_64)
4863061da546Spatrick     strm << "ptrsize:8;";
4864061da546Spatrick   else
4865061da546Spatrick     strm << "ptrsize:" << std::dec << sizeof(void *) << ';';
4866061da546Spatrick 
4867061da546Spatrick #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
4868061da546Spatrick   strm << "default_packet_timeout:10;";
4869061da546Spatrick #endif
4870061da546Spatrick 
4871be691f3bSpatrick   strm << "vm-page-size:" << std::dec << vm_page_size << ";";
4872be691f3bSpatrick 
4873061da546Spatrick   return SendPacket(strm.str());
4874061da546Spatrick }
4875061da546Spatrick 
XMLElementStart(std::ostringstream & s,uint32_t indent,const char * name,bool has_attributes)4876061da546Spatrick void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name,
4877061da546Spatrick                      bool has_attributes) {
4878061da546Spatrick   if (indent)
4879061da546Spatrick     s << INDENT_WITH_SPACES(indent);
4880061da546Spatrick   s << '<' << name;
4881061da546Spatrick   if (!has_attributes)
4882061da546Spatrick     s << '>' << std::endl;
4883061da546Spatrick }
4884061da546Spatrick 
XMLElementStartEndAttributes(std::ostringstream & s,bool empty)4885061da546Spatrick void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) {
4886061da546Spatrick   if (empty)
4887061da546Spatrick     s << '/';
4888061da546Spatrick   s << '>' << std::endl;
4889061da546Spatrick }
4890061da546Spatrick 
XMLElementEnd(std::ostringstream & s,uint32_t indent,const char * name)4891061da546Spatrick void XMLElementEnd(std::ostringstream &s, uint32_t indent, const char *name) {
4892061da546Spatrick   if (indent)
4893061da546Spatrick     s << INDENT_WITH_SPACES(indent);
4894061da546Spatrick   s << '<' << '/' << name << '>' << std::endl;
4895061da546Spatrick }
4896061da546Spatrick 
XMLElementWithStringValue(std::ostringstream & s,uint32_t indent,const char * name,const char * value,bool close=true)4897061da546Spatrick void XMLElementWithStringValue(std::ostringstream &s, uint32_t indent,
4898061da546Spatrick                                const char *name, const char *value,
4899061da546Spatrick                                bool close = true) {
4900061da546Spatrick   if (value) {
4901061da546Spatrick     if (indent)
4902061da546Spatrick       s << INDENT_WITH_SPACES(indent);
4903061da546Spatrick     s << '<' << name << '>' << value;
4904061da546Spatrick     if (close)
4905061da546Spatrick       XMLElementEnd(s, 0, name);
4906061da546Spatrick   }
4907061da546Spatrick }
4908061da546Spatrick 
XMLElementWithUnsignedValue(std::ostringstream & s,uint32_t indent,const char * name,uint64_t value,bool close=true)4909061da546Spatrick void XMLElementWithUnsignedValue(std::ostringstream &s, uint32_t indent,
4910061da546Spatrick                                  const char *name, uint64_t value,
4911061da546Spatrick                                  bool close = true) {
4912061da546Spatrick   if (indent)
4913061da546Spatrick     s << INDENT_WITH_SPACES(indent);
4914061da546Spatrick 
4915061da546Spatrick   s << '<' << name << '>' << DECIMAL << value;
4916061da546Spatrick   if (close)
4917061da546Spatrick     XMLElementEnd(s, 0, name);
4918061da546Spatrick }
4919061da546Spatrick 
XMLAttributeString(std::ostringstream & s,const char * name,const char * value,const char * default_value=NULL)4920061da546Spatrick void XMLAttributeString(std::ostringstream &s, const char *name,
4921061da546Spatrick                         const char *value, const char *default_value = NULL) {
4922061da546Spatrick   if (value) {
4923061da546Spatrick     if (default_value && strcmp(value, default_value) == 0)
4924061da546Spatrick       return; // No need to emit the attribute because it matches the default
4925061da546Spatrick               // value
4926061da546Spatrick     s << ' ' << name << "=\"" << value << "\"";
4927061da546Spatrick   }
4928061da546Spatrick }
4929061da546Spatrick 
XMLAttributeUnsignedDecimal(std::ostringstream & s,const char * name,uint64_t value)4930061da546Spatrick void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name,
4931061da546Spatrick                                  uint64_t value) {
4932061da546Spatrick   s << ' ' << name << "=\"" << DECIMAL << value << "\"";
4933061da546Spatrick }
4934061da546Spatrick 
GenerateTargetXMLRegister(std::ostringstream & s,const uint32_t reg_num,nub_size_t num_reg_sets,const DNBRegisterSetInfo * reg_set_info,const register_map_entry_t & reg)4935061da546Spatrick void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num,
4936061da546Spatrick                                nub_size_t num_reg_sets,
4937061da546Spatrick                                const DNBRegisterSetInfo *reg_set_info,
4938061da546Spatrick                                const register_map_entry_t &reg) {
4939061da546Spatrick   const char *default_lldb_encoding = "uint";
4940061da546Spatrick   const char *lldb_encoding = default_lldb_encoding;
4941061da546Spatrick   const char *gdb_group = "general";
4942061da546Spatrick   const char *default_gdb_type = "int";
4943061da546Spatrick   const char *gdb_type = default_gdb_type;
4944061da546Spatrick   const char *default_lldb_format = "hex";
4945061da546Spatrick   const char *lldb_format = default_lldb_format;
4946061da546Spatrick 
4947061da546Spatrick   switch (reg.nub_info.type) {
4948061da546Spatrick   case Uint:
4949061da546Spatrick     lldb_encoding = "uint";
4950061da546Spatrick     break;
4951061da546Spatrick   case Sint:
4952061da546Spatrick     lldb_encoding = "sint";
4953061da546Spatrick     break;
4954061da546Spatrick   case IEEE754:
4955061da546Spatrick     lldb_encoding = "ieee754";
4956061da546Spatrick     if (reg.nub_info.set > 0)
4957061da546Spatrick       gdb_group = "float";
4958061da546Spatrick     break;
4959061da546Spatrick   case Vector:
4960061da546Spatrick     lldb_encoding = "vector";
4961061da546Spatrick     if (reg.nub_info.set > 0)
4962061da546Spatrick       gdb_group = "vector";
4963061da546Spatrick     break;
4964061da546Spatrick   }
4965061da546Spatrick 
4966061da546Spatrick   switch (reg.nub_info.format) {
4967061da546Spatrick   case Binary:
4968061da546Spatrick     lldb_format = "binary";
4969061da546Spatrick     break;
4970061da546Spatrick   case Decimal:
4971061da546Spatrick     lldb_format = "decimal";
4972061da546Spatrick     break;
4973061da546Spatrick   case Hex:
4974061da546Spatrick     lldb_format = "hex";
4975061da546Spatrick     break;
4976061da546Spatrick   case Float:
4977061da546Spatrick     gdb_type = "float";
4978061da546Spatrick     lldb_format = "float";
4979061da546Spatrick     break;
4980061da546Spatrick   case VectorOfSInt8:
4981061da546Spatrick     gdb_type = "float";
4982061da546Spatrick     lldb_format = "vector-sint8";
4983061da546Spatrick     break;
4984061da546Spatrick   case VectorOfUInt8:
4985061da546Spatrick     gdb_type = "float";
4986061da546Spatrick     lldb_format = "vector-uint8";
4987061da546Spatrick     break;
4988061da546Spatrick   case VectorOfSInt16:
4989061da546Spatrick     gdb_type = "float";
4990061da546Spatrick     lldb_format = "vector-sint16";
4991061da546Spatrick     break;
4992061da546Spatrick   case VectorOfUInt16:
4993061da546Spatrick     gdb_type = "float";
4994061da546Spatrick     lldb_format = "vector-uint16";
4995061da546Spatrick     break;
4996061da546Spatrick   case VectorOfSInt32:
4997061da546Spatrick     gdb_type = "float";
4998061da546Spatrick     lldb_format = "vector-sint32";
4999061da546Spatrick     break;
5000061da546Spatrick   case VectorOfUInt32:
5001061da546Spatrick     gdb_type = "float";
5002061da546Spatrick     lldb_format = "vector-uint32";
5003061da546Spatrick     break;
5004061da546Spatrick   case VectorOfFloat32:
5005061da546Spatrick     gdb_type = "float";
5006061da546Spatrick     lldb_format = "vector-float32";
5007061da546Spatrick     break;
5008061da546Spatrick   case VectorOfUInt128:
5009061da546Spatrick     gdb_type = "float";
5010061da546Spatrick     lldb_format = "vector-uint128";
5011061da546Spatrick     break;
5012061da546Spatrick   };
5013061da546Spatrick 
5014061da546Spatrick   uint32_t indent = 2;
5015061da546Spatrick 
5016061da546Spatrick   XMLElementStart(s, indent, "reg", true);
5017061da546Spatrick   XMLAttributeString(s, "name", reg.nub_info.name);
5018061da546Spatrick   XMLAttributeUnsignedDecimal(s, "regnum", reg_num);
5019061da546Spatrick   XMLAttributeUnsignedDecimal(s, "offset", reg.offset);
5020061da546Spatrick   XMLAttributeUnsignedDecimal(s, "bitsize", reg.nub_info.size * 8);
5021061da546Spatrick   XMLAttributeString(s, "group", gdb_group);
5022061da546Spatrick   XMLAttributeString(s, "type", gdb_type, default_gdb_type);
5023061da546Spatrick   XMLAttributeString(s, "altname", reg.nub_info.alt);
5024061da546Spatrick   XMLAttributeString(s, "encoding", lldb_encoding, default_lldb_encoding);
5025061da546Spatrick   XMLAttributeString(s, "format", lldb_format, default_lldb_format);
5026061da546Spatrick   XMLAttributeUnsignedDecimal(s, "group_id", reg.nub_info.set);
5027061da546Spatrick   if (reg.nub_info.reg_ehframe != INVALID_NUB_REGNUM)
5028061da546Spatrick     XMLAttributeUnsignedDecimal(s, "ehframe_regnum", reg.nub_info.reg_ehframe);
5029061da546Spatrick   if (reg.nub_info.reg_dwarf != INVALID_NUB_REGNUM)
5030061da546Spatrick     XMLAttributeUnsignedDecimal(s, "dwarf_regnum", reg.nub_info.reg_dwarf);
5031061da546Spatrick 
5032061da546Spatrick   const char *lldb_generic = NULL;
5033061da546Spatrick   switch (reg.nub_info.reg_generic) {
5034061da546Spatrick   case GENERIC_REGNUM_FP:
5035061da546Spatrick     lldb_generic = "fp";
5036061da546Spatrick     break;
5037061da546Spatrick   case GENERIC_REGNUM_PC:
5038061da546Spatrick     lldb_generic = "pc";
5039061da546Spatrick     break;
5040061da546Spatrick   case GENERIC_REGNUM_SP:
5041061da546Spatrick     lldb_generic = "sp";
5042061da546Spatrick     break;
5043061da546Spatrick   case GENERIC_REGNUM_RA:
5044061da546Spatrick     lldb_generic = "ra";
5045061da546Spatrick     break;
5046061da546Spatrick   case GENERIC_REGNUM_FLAGS:
5047061da546Spatrick     lldb_generic = "flags";
5048061da546Spatrick     break;
5049061da546Spatrick   case GENERIC_REGNUM_ARG1:
5050061da546Spatrick     lldb_generic = "arg1";
5051061da546Spatrick     break;
5052061da546Spatrick   case GENERIC_REGNUM_ARG2:
5053061da546Spatrick     lldb_generic = "arg2";
5054061da546Spatrick     break;
5055061da546Spatrick   case GENERIC_REGNUM_ARG3:
5056061da546Spatrick     lldb_generic = "arg3";
5057061da546Spatrick     break;
5058061da546Spatrick   case GENERIC_REGNUM_ARG4:
5059061da546Spatrick     lldb_generic = "arg4";
5060061da546Spatrick     break;
5061061da546Spatrick   case GENERIC_REGNUM_ARG5:
5062061da546Spatrick     lldb_generic = "arg5";
5063061da546Spatrick     break;
5064061da546Spatrick   case GENERIC_REGNUM_ARG6:
5065061da546Spatrick     lldb_generic = "arg6";
5066061da546Spatrick     break;
5067061da546Spatrick   case GENERIC_REGNUM_ARG7:
5068061da546Spatrick     lldb_generic = "arg7";
5069061da546Spatrick     break;
5070061da546Spatrick   case GENERIC_REGNUM_ARG8:
5071061da546Spatrick     lldb_generic = "arg8";
5072061da546Spatrick     break;
5073061da546Spatrick   default:
5074061da546Spatrick     break;
5075061da546Spatrick   }
5076061da546Spatrick   XMLAttributeString(s, "generic", lldb_generic);
5077061da546Spatrick 
5078061da546Spatrick   bool empty = reg.value_regnums.empty() && reg.invalidate_regnums.empty();
5079061da546Spatrick   if (!empty) {
5080061da546Spatrick     if (!reg.value_regnums.empty()) {
5081061da546Spatrick       std::ostringstream regnums;
5082061da546Spatrick       bool first = true;
5083061da546Spatrick       regnums << DECIMAL;
5084061da546Spatrick       for (auto regnum : reg.value_regnums) {
5085061da546Spatrick         if (!first)
5086061da546Spatrick           regnums << ',';
5087061da546Spatrick         regnums << regnum;
5088061da546Spatrick         first = false;
5089061da546Spatrick       }
5090061da546Spatrick       XMLAttributeString(s, "value_regnums", regnums.str().c_str());
5091061da546Spatrick     }
5092061da546Spatrick 
5093061da546Spatrick     if (!reg.invalidate_regnums.empty()) {
5094061da546Spatrick       std::ostringstream regnums;
5095061da546Spatrick       bool first = true;
5096061da546Spatrick       regnums << DECIMAL;
5097061da546Spatrick       for (auto regnum : reg.invalidate_regnums) {
5098061da546Spatrick         if (!first)
5099061da546Spatrick           regnums << ',';
5100061da546Spatrick         regnums << regnum;
5101061da546Spatrick         first = false;
5102061da546Spatrick       }
5103061da546Spatrick       XMLAttributeString(s, "invalidate_regnums", regnums.str().c_str());
5104061da546Spatrick     }
5105061da546Spatrick   }
5106061da546Spatrick   XMLElementStartEndAttributes(s, true);
5107061da546Spatrick }
5108061da546Spatrick 
GenerateTargetXMLRegisters(std::ostringstream & s)5109061da546Spatrick void GenerateTargetXMLRegisters(std::ostringstream &s) {
5110061da546Spatrick   nub_size_t num_reg_sets = 0;
5111061da546Spatrick   const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets);
5112061da546Spatrick 
5113061da546Spatrick   uint32_t cputype = DNBGetRegisterCPUType();
5114061da546Spatrick   if (cputype) {
5115061da546Spatrick     XMLElementStart(s, 0, "feature", true);
5116061da546Spatrick     std::ostringstream name_strm;
5117061da546Spatrick     name_strm << "com.apple.debugserver." << GetArchName(cputype, 0);
5118061da546Spatrick     XMLAttributeString(s, "name", name_strm.str().c_str());
5119061da546Spatrick     XMLElementStartEndAttributes(s, false);
5120061da546Spatrick     for (uint32_t reg_num = 0; reg_num < g_num_reg_entries; ++reg_num)
5121061da546Spatrick     //        for (const auto &reg: g_dynamic_register_map)
5122061da546Spatrick     {
5123061da546Spatrick       GenerateTargetXMLRegister(s, reg_num, num_reg_sets, reg_sets,
5124061da546Spatrick                                 g_reg_entries[reg_num]);
5125061da546Spatrick     }
5126061da546Spatrick     XMLElementEnd(s, 0, "feature");
5127061da546Spatrick 
5128061da546Spatrick     if (num_reg_sets > 0) {
5129061da546Spatrick       XMLElementStart(s, 0, "groups", false);
5130061da546Spatrick       for (uint32_t set = 1; set < num_reg_sets; ++set) {
5131061da546Spatrick         XMLElementStart(s, 2, "group", true);
5132061da546Spatrick         XMLAttributeUnsignedDecimal(s, "id", set);
5133061da546Spatrick         XMLAttributeString(s, "name", reg_sets[set].name);
5134061da546Spatrick         XMLElementStartEndAttributes(s, true);
5135061da546Spatrick       }
5136061da546Spatrick       XMLElementEnd(s, 0, "groups");
5137061da546Spatrick     }
5138061da546Spatrick   }
5139061da546Spatrick }
5140061da546Spatrick 
5141061da546Spatrick static const char *g_target_xml_header = R"(<?xml version="1.0"?>
5142061da546Spatrick <target version="1.0">)";
5143061da546Spatrick 
5144061da546Spatrick static const char *g_target_xml_footer = "</target>";
5145061da546Spatrick 
5146061da546Spatrick static std::string g_target_xml;
5147061da546Spatrick 
UpdateTargetXML()5148061da546Spatrick void UpdateTargetXML() {
5149061da546Spatrick   std::ostringstream s;
5150061da546Spatrick   s << g_target_xml_header << std::endl;
5151061da546Spatrick 
5152061da546Spatrick   // Set the architecture
5153061da546Spatrick   //
5154061da546Spatrick   // On raw targets (no OS, vendor info), I've seen replies like
5155061da546Spatrick   // <architecture>i386:x86-64</architecture> (for x86_64 systems - from vmware)
5156061da546Spatrick   // <architecture>arm</architecture> (for an unspecified arm device - from a Segger JLink)
5157061da546Spatrick   // For good interop, I'm not sure what's expected here.  e.g. will anyone understand
5158061da546Spatrick   // <architecture>x86_64</architecture> ? Or is i386:x86_64 the expected phrasing?
5159061da546Spatrick   //
5160061da546Spatrick   // s << "<architecture>" << arch "</architecture>" << std::endl;
5161061da546Spatrick 
5162061da546Spatrick   // Set the OSABI
5163061da546Spatrick   // s << "<osabi>abi-name</osabi>"
5164061da546Spatrick 
5165061da546Spatrick   GenerateTargetXMLRegisters(s);
5166061da546Spatrick 
5167061da546Spatrick   s << g_target_xml_footer << std::endl;
5168061da546Spatrick 
5169061da546Spatrick   // Save the XML output in case it gets retrieved in chunks
5170061da546Spatrick   g_target_xml = s.str();
5171061da546Spatrick }
5172061da546Spatrick 
HandlePacket_qXfer(const char * command)5173061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qXfer(const char *command) {
5174061da546Spatrick   const char *p = command;
5175061da546Spatrick   p += strlen("qXfer:");
5176061da546Spatrick   const char *sep = strchr(p, ':');
5177061da546Spatrick   if (sep) {
5178061da546Spatrick     std::string object(p, sep - p); // "auxv", "backtrace", "features", etc
5179061da546Spatrick     p = sep + 1;
5180061da546Spatrick     sep = strchr(p, ':');
5181061da546Spatrick     if (sep) {
5182061da546Spatrick       std::string rw(p, sep - p); // "read" or "write"
5183061da546Spatrick       p = sep + 1;
5184061da546Spatrick       sep = strchr(p, ':');
5185061da546Spatrick       if (sep) {
5186061da546Spatrick         std::string annex(p, sep - p); // "read" or "write"
5187061da546Spatrick 
5188061da546Spatrick         p = sep + 1;
5189061da546Spatrick         sep = strchr(p, ',');
5190061da546Spatrick         if (sep) {
5191061da546Spatrick           std::string offset_str(p, sep - p); // read the length as a string
5192061da546Spatrick           p = sep + 1;
5193061da546Spatrick           std::string length_str(p); // read the offset as a string
5194061da546Spatrick           char *end = nullptr;
5195061da546Spatrick           const uint64_t offset = strtoul(offset_str.c_str(), &end,
5196061da546Spatrick                                           16); // convert offset_str to a offset
5197061da546Spatrick           if (*end == '\0') {
5198061da546Spatrick             const uint64_t length = strtoul(
5199061da546Spatrick                 length_str.c_str(), &end, 16); // convert length_str to a length
5200061da546Spatrick             if (*end == '\0') {
5201061da546Spatrick               if (object == "features" && rw == "read" &&
5202061da546Spatrick                   annex == "target.xml") {
5203061da546Spatrick                 std::ostringstream xml_out;
5204061da546Spatrick 
5205061da546Spatrick                 if (offset == 0) {
5206061da546Spatrick                   InitializeRegisters(true);
5207061da546Spatrick 
5208061da546Spatrick                   UpdateTargetXML();
5209061da546Spatrick                   if (g_target_xml.empty())
5210061da546Spatrick                     return SendPacket("E83");
5211061da546Spatrick 
5212061da546Spatrick                   if (length > g_target_xml.size()) {
5213061da546Spatrick                     xml_out << 'l'; // No more data
5214061da546Spatrick                     xml_out << binary_encode_string(g_target_xml);
5215061da546Spatrick                   } else {
5216061da546Spatrick                     xml_out << 'm'; // More data needs to be read with a
5217061da546Spatrick                                     // subsequent call
5218061da546Spatrick                     xml_out << binary_encode_string(
5219061da546Spatrick                         std::string(g_target_xml, offset, length));
5220061da546Spatrick                   }
5221061da546Spatrick                 } else {
5222061da546Spatrick                   // Retrieving target XML in chunks
5223061da546Spatrick                   if (offset < g_target_xml.size()) {
5224061da546Spatrick                     std::string chunk(g_target_xml, offset, length);
5225061da546Spatrick                     if (chunk.size() < length)
5226061da546Spatrick                       xml_out << 'l'; // No more data
5227061da546Spatrick                     else
5228061da546Spatrick                       xml_out << 'm'; // More data needs to be read with a
5229061da546Spatrick                                       // subsequent call
5230061da546Spatrick                     xml_out << binary_encode_string(chunk.data());
5231061da546Spatrick                   }
5232061da546Spatrick                 }
5233061da546Spatrick                 return SendPacket(xml_out.str());
5234061da546Spatrick               }
5235061da546Spatrick               // Well formed, put not supported
5236061da546Spatrick               return HandlePacket_UNIMPLEMENTED(command);
5237061da546Spatrick             }
5238061da546Spatrick           }
5239061da546Spatrick         }
5240061da546Spatrick       } else {
5241061da546Spatrick         SendPacket("E85");
5242061da546Spatrick       }
5243061da546Spatrick     } else {
5244061da546Spatrick       SendPacket("E86");
5245061da546Spatrick     }
5246061da546Spatrick   }
5247061da546Spatrick   return SendPacket("E82");
5248061da546Spatrick }
5249061da546Spatrick 
HandlePacket_qGDBServerVersion(const char * p)5250061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qGDBServerVersion(const char *p) {
5251061da546Spatrick   std::ostringstream strm;
5252061da546Spatrick 
5253061da546Spatrick #if defined(DEBUGSERVER_PROGRAM_NAME)
5254061da546Spatrick   strm << "name:" DEBUGSERVER_PROGRAM_NAME ";";
5255061da546Spatrick #else
5256061da546Spatrick   strm << "name:debugserver;";
5257061da546Spatrick #endif
5258061da546Spatrick   strm << "version:" << DEBUGSERVER_VERSION_NUM << ";";
5259061da546Spatrick 
5260061da546Spatrick   return SendPacket(strm.str());
5261061da546Spatrick }
5262061da546Spatrick 
HandlePacket_jGetDyldProcessState(const char * p)5263*f6aab3d8Srobert rnb_err_t RNBRemote::HandlePacket_jGetDyldProcessState(const char *p) {
5264*f6aab3d8Srobert   const nub_process_t pid = m_ctx.ProcessID();
5265*f6aab3d8Srobert   if (pid == INVALID_NUB_PROCESS)
5266*f6aab3d8Srobert     return SendPacket("E87");
5267*f6aab3d8Srobert 
5268*f6aab3d8Srobert   JSONGenerator::ObjectSP dyld_state_sp = DNBGetDyldProcessState(pid);
5269*f6aab3d8Srobert   if (dyld_state_sp) {
5270*f6aab3d8Srobert     std::ostringstream strm;
5271*f6aab3d8Srobert     dyld_state_sp->DumpBinaryEscaped(strm);
5272*f6aab3d8Srobert     dyld_state_sp->Clear();
5273*f6aab3d8Srobert     if (strm.str().size() > 0)
5274*f6aab3d8Srobert       return SendPacket(strm.str());
5275*f6aab3d8Srobert   }
5276*f6aab3d8Srobert   return SendPacket("E88");
5277*f6aab3d8Srobert }
5278*f6aab3d8Srobert 
5279061da546Spatrick // A helper function that retrieves a single integer value from
5280061da546Spatrick // a one-level-deep JSON dictionary of key-value pairs.  e.g.
5281061da546Spatrick // jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}]
5282061da546Spatrick //
get_integer_value_for_key_name_from_json(const char * key,const char * json_string)5283061da546Spatrick uint64_t get_integer_value_for_key_name_from_json(const char *key,
5284061da546Spatrick                                                   const char *json_string) {
5285061da546Spatrick   uint64_t retval = INVALID_NUB_ADDRESS;
5286061da546Spatrick   std::string key_with_quotes = "\"";
5287061da546Spatrick   key_with_quotes += key;
5288061da546Spatrick   key_with_quotes += "\"";
5289061da546Spatrick   const char *c = strstr(json_string, key_with_quotes.c_str());
5290061da546Spatrick   if (c) {
5291061da546Spatrick     c += key_with_quotes.size();
5292061da546Spatrick 
5293061da546Spatrick     while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5294061da546Spatrick       c++;
5295061da546Spatrick 
5296061da546Spatrick     if (*c == ':') {
5297061da546Spatrick       c++;
5298061da546Spatrick 
5299061da546Spatrick       while (*c != '\0' &&
5300061da546Spatrick              (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5301061da546Spatrick         c++;
5302061da546Spatrick 
5303061da546Spatrick       errno = 0;
5304061da546Spatrick       retval = strtoul(c, NULL, 10);
5305061da546Spatrick       if (errno != 0) {
5306061da546Spatrick         retval = INVALID_NUB_ADDRESS;
5307061da546Spatrick       }
5308061da546Spatrick     }
5309061da546Spatrick   }
5310061da546Spatrick   return retval;
5311061da546Spatrick }
5312061da546Spatrick 
5313061da546Spatrick // A helper function that retrieves a boolean value from
5314061da546Spatrick // a one-level-deep JSON dictionary of key-value pairs.  e.g.
5315061da546Spatrick // jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}]
5316061da546Spatrick 
5317061da546Spatrick // Returns true if it was able to find the key name, and sets the 'value'
5318061da546Spatrick // argument to the value found.
5319061da546Spatrick 
get_boolean_value_for_key_name_from_json(const char * key,const char * json_string,bool & value)5320061da546Spatrick bool get_boolean_value_for_key_name_from_json(const char *key,
5321061da546Spatrick                                               const char *json_string,
5322061da546Spatrick                                               bool &value) {
5323061da546Spatrick   std::string key_with_quotes = "\"";
5324061da546Spatrick   key_with_quotes += key;
5325061da546Spatrick   key_with_quotes += "\"";
5326061da546Spatrick   const char *c = strstr(json_string, key_with_quotes.c_str());
5327061da546Spatrick   if (c) {
5328061da546Spatrick     c += key_with_quotes.size();
5329061da546Spatrick 
5330061da546Spatrick     while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5331061da546Spatrick       c++;
5332061da546Spatrick 
5333061da546Spatrick     if (*c == ':') {
5334061da546Spatrick       c++;
5335061da546Spatrick 
5336061da546Spatrick       while (*c != '\0' &&
5337061da546Spatrick              (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5338061da546Spatrick         c++;
5339061da546Spatrick 
5340061da546Spatrick       if (strncmp(c, "true", 4) == 0) {
5341061da546Spatrick         value = true;
5342061da546Spatrick         return true;
5343061da546Spatrick       } else if (strncmp(c, "false", 5) == 0) {
5344061da546Spatrick         value = false;
5345061da546Spatrick         return true;
5346061da546Spatrick       }
5347061da546Spatrick     }
5348061da546Spatrick   }
5349061da546Spatrick   return false;
5350061da546Spatrick }
5351061da546Spatrick 
5352061da546Spatrick // A helper function that reads an array of uint64_t's from
5353061da546Spatrick // a one-level-deep JSON dictionary of key-value pairs.  e.g.
5354061da546Spatrick // jGetLoadedDynamicLibrariesInfos:{"solib_addrs":[31345823,7768020384,7310483024]}]
5355061da546Spatrick 
5356061da546Spatrick // Returns true if it was able to find the key name, false if it did not.
5357061da546Spatrick // "ints" will have all integers found in the array appended to it.
5358061da546Spatrick 
get_array_of_ints_value_for_key_name_from_json(const char * key,const char * json_string,std::vector<uint64_t> & ints)5359061da546Spatrick bool get_array_of_ints_value_for_key_name_from_json(
5360061da546Spatrick     const char *key, const char *json_string, std::vector<uint64_t> &ints) {
5361061da546Spatrick   std::string key_with_quotes = "\"";
5362061da546Spatrick   key_with_quotes += key;
5363061da546Spatrick   key_with_quotes += "\"";
5364061da546Spatrick   const char *c = strstr(json_string, key_with_quotes.c_str());
5365061da546Spatrick   if (c) {
5366061da546Spatrick     c += key_with_quotes.size();
5367061da546Spatrick 
5368061da546Spatrick     while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5369061da546Spatrick       c++;
5370061da546Spatrick 
5371061da546Spatrick     if (*c == ':') {
5372061da546Spatrick       c++;
5373061da546Spatrick 
5374061da546Spatrick       while (*c != '\0' &&
5375061da546Spatrick              (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5376061da546Spatrick         c++;
5377061da546Spatrick 
5378061da546Spatrick       if (*c == '[') {
5379061da546Spatrick         c++;
5380061da546Spatrick         while (*c != '\0' &&
5381061da546Spatrick                (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5382061da546Spatrick           c++;
5383061da546Spatrick         while (true) {
5384061da546Spatrick           if (!isdigit(*c)) {
5385061da546Spatrick             return true;
5386061da546Spatrick           }
5387061da546Spatrick 
5388061da546Spatrick           errno = 0;
5389061da546Spatrick           char *endptr;
5390061da546Spatrick           uint64_t value = strtoul(c, &endptr, 10);
5391061da546Spatrick           if (errno == 0) {
5392061da546Spatrick             ints.push_back(value);
5393061da546Spatrick           } else {
5394061da546Spatrick             break;
5395061da546Spatrick           }
5396061da546Spatrick           if (endptr == c || endptr == nullptr || *endptr == '\0') {
5397061da546Spatrick             break;
5398061da546Spatrick           }
5399061da546Spatrick           c = endptr;
5400061da546Spatrick 
5401061da546Spatrick           while (*c != '\0' &&
5402061da546Spatrick                  (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5403061da546Spatrick             c++;
5404061da546Spatrick           if (*c == ',')
5405061da546Spatrick             c++;
5406061da546Spatrick           while (*c != '\0' &&
5407061da546Spatrick                  (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
5408061da546Spatrick             c++;
5409061da546Spatrick           if (*c == ']') {
5410061da546Spatrick             return true;
5411061da546Spatrick           }
5412061da546Spatrick         }
5413061da546Spatrick       }
5414061da546Spatrick     }
5415061da546Spatrick   }
5416061da546Spatrick   return false;
5417061da546Spatrick }
5418061da546Spatrick 
5419061da546Spatrick JSONGenerator::ObjectSP
GetJSONThreadsInfo(bool threads_with_valid_stop_info_only)5420061da546Spatrick RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only) {
5421061da546Spatrick   JSONGenerator::ArraySP threads_array_sp;
5422061da546Spatrick   if (m_ctx.HasValidProcessID()) {
5423061da546Spatrick     threads_array_sp = std::make_shared<JSONGenerator::Array>();
5424061da546Spatrick 
5425061da546Spatrick     nub_process_t pid = m_ctx.ProcessID();
5426061da546Spatrick 
5427061da546Spatrick     nub_size_t numthreads = DNBProcessGetNumThreads(pid);
5428061da546Spatrick     for (nub_size_t i = 0; i < numthreads; ++i) {
5429061da546Spatrick       nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, i);
5430061da546Spatrick 
5431061da546Spatrick       struct DNBThreadStopInfo tid_stop_info;
5432061da546Spatrick 
5433061da546Spatrick       const bool stop_info_valid =
5434061da546Spatrick           DNBThreadGetStopReason(pid, tid, &tid_stop_info);
5435061da546Spatrick 
5436061da546Spatrick       // If we are doing stop info only, then we only show threads that have a
5437061da546Spatrick       // valid stop reason
5438061da546Spatrick       if (threads_with_valid_stop_info_only) {
5439061da546Spatrick         if (!stop_info_valid || tid_stop_info.reason == eStopTypeInvalid)
5440061da546Spatrick           continue;
5441061da546Spatrick       }
5442061da546Spatrick 
5443061da546Spatrick       JSONGenerator::DictionarySP thread_dict_sp(
5444061da546Spatrick           new JSONGenerator::Dictionary());
5445061da546Spatrick       thread_dict_sp->AddIntegerItem("tid", tid);
5446061da546Spatrick 
5447061da546Spatrick       std::string reason_value("none");
5448061da546Spatrick 
5449061da546Spatrick       if (stop_info_valid) {
5450061da546Spatrick         switch (tid_stop_info.reason) {
5451061da546Spatrick         case eStopTypeInvalid:
5452061da546Spatrick           break;
5453061da546Spatrick 
5454061da546Spatrick         case eStopTypeSignal:
5455061da546Spatrick           if (tid_stop_info.details.signal.signo != 0) {
5456061da546Spatrick             thread_dict_sp->AddIntegerItem("signal",
5457061da546Spatrick                                            tid_stop_info.details.signal.signo);
5458061da546Spatrick             reason_value = "signal";
5459061da546Spatrick           }
5460061da546Spatrick           break;
5461061da546Spatrick 
5462061da546Spatrick         case eStopTypeException:
5463061da546Spatrick           if (tid_stop_info.details.exception.type != 0) {
5464061da546Spatrick             reason_value = "exception";
5465061da546Spatrick             thread_dict_sp->AddIntegerItem(
5466061da546Spatrick                 "metype", tid_stop_info.details.exception.type);
5467061da546Spatrick             JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array());
5468061da546Spatrick             for (nub_size_t i = 0;
5469061da546Spatrick                  i < tid_stop_info.details.exception.data_count; ++i) {
5470061da546Spatrick               medata_array_sp->AddItem(
5471061da546Spatrick                   JSONGenerator::IntegerSP(new JSONGenerator::Integer(
5472061da546Spatrick                       tid_stop_info.details.exception.data[i])));
5473061da546Spatrick             }
5474061da546Spatrick             thread_dict_sp->AddItem("medata", medata_array_sp);
5475061da546Spatrick           }
5476061da546Spatrick           break;
5477061da546Spatrick 
5478061da546Spatrick         case eStopTypeExec:
5479061da546Spatrick           reason_value = "exec";
5480061da546Spatrick           break;
5481061da546Spatrick         }
5482061da546Spatrick       }
5483061da546Spatrick 
5484061da546Spatrick       thread_dict_sp->AddStringItem("reason", reason_value);
5485061da546Spatrick 
5486061da546Spatrick       if (!threads_with_valid_stop_info_only) {
5487061da546Spatrick         const char *thread_name = DNBThreadGetName(pid, tid);
5488061da546Spatrick         if (thread_name && thread_name[0])
5489061da546Spatrick           thread_dict_sp->AddStringItem("name", thread_name);
5490061da546Spatrick 
5491061da546Spatrick         thread_identifier_info_data_t thread_ident_info;
5492061da546Spatrick         if (DNBThreadGetIdentifierInfo(pid, tid, &thread_ident_info)) {
5493061da546Spatrick           if (thread_ident_info.dispatch_qaddr != 0) {
5494061da546Spatrick             thread_dict_sp->AddIntegerItem("qaddr",
5495061da546Spatrick                                            thread_ident_info.dispatch_qaddr);
5496061da546Spatrick 
5497061da546Spatrick             const DispatchQueueOffsets *dispatch_queue_offsets =
5498061da546Spatrick                 GetDispatchQueueOffsets();
5499061da546Spatrick             if (dispatch_queue_offsets) {
5500061da546Spatrick               std::string queue_name;
5501061da546Spatrick               uint64_t queue_width = 0;
5502061da546Spatrick               uint64_t queue_serialnum = 0;
5503061da546Spatrick               nub_addr_t dispatch_queue_t = INVALID_NUB_ADDRESS;
5504061da546Spatrick               dispatch_queue_offsets->GetThreadQueueInfo(
5505061da546Spatrick                   pid, thread_ident_info.dispatch_qaddr, dispatch_queue_t,
5506061da546Spatrick                   queue_name, queue_width, queue_serialnum);
5507061da546Spatrick               if (dispatch_queue_t == 0 && queue_name.empty() &&
5508061da546Spatrick                   queue_serialnum == 0) {
5509061da546Spatrick                 thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue",
5510061da546Spatrick                                                false);
5511061da546Spatrick               } else {
5512061da546Spatrick                 thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue",
5513061da546Spatrick                                                true);
5514061da546Spatrick               }
5515061da546Spatrick               if (dispatch_queue_t != INVALID_NUB_ADDRESS &&
5516061da546Spatrick                   dispatch_queue_t != 0)
5517061da546Spatrick                 thread_dict_sp->AddIntegerItem("dispatch_queue_t",
5518061da546Spatrick                                                dispatch_queue_t);
5519061da546Spatrick               if (!queue_name.empty())
5520061da546Spatrick                 thread_dict_sp->AddStringItem("qname", queue_name);
5521061da546Spatrick               if (queue_width == 1)
5522061da546Spatrick                 thread_dict_sp->AddStringItem("qkind", "serial");
5523061da546Spatrick               else if (queue_width > 1)
5524061da546Spatrick                 thread_dict_sp->AddStringItem("qkind", "concurrent");
5525061da546Spatrick               if (queue_serialnum > 0)
5526061da546Spatrick                 thread_dict_sp->AddIntegerItem("qserialnum", queue_serialnum);
5527061da546Spatrick             }
5528061da546Spatrick           }
5529061da546Spatrick         }
5530061da546Spatrick 
5531061da546Spatrick         DNBRegisterValue reg_value;
5532061da546Spatrick 
5533061da546Spatrick         if (g_reg_entries != NULL) {
5534061da546Spatrick           JSONGenerator::DictionarySP registers_dict_sp(
5535061da546Spatrick               new JSONGenerator::Dictionary());
5536061da546Spatrick 
5537061da546Spatrick           for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) {
5538061da546Spatrick             // Expedite all registers in the first register set that aren't
5539061da546Spatrick             // contained in other registers
5540061da546Spatrick             if (g_reg_entries[reg].nub_info.set == 1 &&
5541061da546Spatrick                 g_reg_entries[reg].nub_info.value_regs == NULL) {
5542061da546Spatrick               if (!DNBThreadGetRegisterValueByID(
5543061da546Spatrick                       pid, tid, g_reg_entries[reg].nub_info.set,
5544061da546Spatrick                       g_reg_entries[reg].nub_info.reg, &reg_value))
5545061da546Spatrick                 continue;
5546061da546Spatrick 
5547061da546Spatrick               std::ostringstream reg_num;
5548061da546Spatrick               reg_num << std::dec << g_reg_entries[reg].debugserver_regnum;
5549061da546Spatrick               // Encode native byte ordered bytes as hex ascii
5550061da546Spatrick               registers_dict_sp->AddBytesAsHexASCIIString(
5551061da546Spatrick                   reg_num.str(), reg_value.value.v_uint8,
5552061da546Spatrick                   g_reg_entries[reg].nub_info.size);
5553061da546Spatrick             }
5554061da546Spatrick           }
5555061da546Spatrick           thread_dict_sp->AddItem("registers", registers_dict_sp);
5556061da546Spatrick         }
5557061da546Spatrick 
5558061da546Spatrick         // Add expedited stack memory so stack backtracing doesn't need to read
5559061da546Spatrick         // anything from the
5560061da546Spatrick         // frame pointer chain.
5561061da546Spatrick         StackMemoryMap stack_mmap;
5562061da546Spatrick         ReadStackMemory(pid, tid, stack_mmap);
5563061da546Spatrick         if (!stack_mmap.empty()) {
5564061da546Spatrick           JSONGenerator::ArraySP memory_array_sp(new JSONGenerator::Array());
5565061da546Spatrick 
5566061da546Spatrick           for (const auto &stack_memory : stack_mmap) {
5567061da546Spatrick             JSONGenerator::DictionarySP stack_memory_sp(
5568061da546Spatrick                 new JSONGenerator::Dictionary());
5569061da546Spatrick             stack_memory_sp->AddIntegerItem("address", stack_memory.first);
5570061da546Spatrick             stack_memory_sp->AddBytesAsHexASCIIString(
5571061da546Spatrick                 "bytes", stack_memory.second.bytes, stack_memory.second.length);
5572061da546Spatrick             memory_array_sp->AddItem(stack_memory_sp);
5573061da546Spatrick           }
5574061da546Spatrick           thread_dict_sp->AddItem("memory", memory_array_sp);
5575061da546Spatrick         }
5576061da546Spatrick       }
5577061da546Spatrick 
5578061da546Spatrick       threads_array_sp->AddItem(thread_dict_sp);
5579061da546Spatrick     }
5580061da546Spatrick   }
5581061da546Spatrick   return threads_array_sp;
5582061da546Spatrick }
5583061da546Spatrick 
HandlePacket_jThreadsInfo(const char * p)5584061da546Spatrick rnb_err_t RNBRemote::HandlePacket_jThreadsInfo(const char *p) {
5585061da546Spatrick   JSONGenerator::ObjectSP threads_info_sp;
5586061da546Spatrick   std::ostringstream json;
5587061da546Spatrick   std::ostringstream reply_strm;
5588061da546Spatrick   // If we haven't run the process yet, return an error.
5589061da546Spatrick   if (m_ctx.HasValidProcessID()) {
5590061da546Spatrick     const bool threads_with_valid_stop_info_only = false;
5591061da546Spatrick     JSONGenerator::ObjectSP threads_info_sp =
5592061da546Spatrick         GetJSONThreadsInfo(threads_with_valid_stop_info_only);
5593061da546Spatrick 
5594061da546Spatrick     if (threads_info_sp) {
5595061da546Spatrick       std::ostringstream strm;
5596*f6aab3d8Srobert       threads_info_sp->DumpBinaryEscaped(strm);
5597*f6aab3d8Srobert       threads_info_sp->Clear();
5598*f6aab3d8Srobert       if (strm.str().size() > 0)
5599*f6aab3d8Srobert         return SendPacket(strm.str());
5600061da546Spatrick     }
5601061da546Spatrick   }
5602061da546Spatrick   return SendPacket("E85");
5603061da546Spatrick }
5604061da546Spatrick 
HandlePacket_jThreadExtendedInfo(const char * p)5605061da546Spatrick rnb_err_t RNBRemote::HandlePacket_jThreadExtendedInfo(const char *p) {
5606061da546Spatrick   nub_process_t pid;
5607061da546Spatrick   std::ostringstream json;
5608061da546Spatrick   // If we haven't run the process yet, return an error.
5609061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
5610061da546Spatrick     return SendPacket("E81");
5611061da546Spatrick   }
5612061da546Spatrick 
5613061da546Spatrick   pid = m_ctx.ProcessID();
5614061da546Spatrick 
5615061da546Spatrick   const char thread_extended_info_str[] = {"jThreadExtendedInfo:{"};
5616061da546Spatrick   if (strncmp(p, thread_extended_info_str,
5617061da546Spatrick               sizeof(thread_extended_info_str) - 1) == 0) {
5618061da546Spatrick     p += strlen(thread_extended_info_str);
5619061da546Spatrick 
5620061da546Spatrick     uint64_t tid = get_integer_value_for_key_name_from_json("thread", p);
5621061da546Spatrick     uint64_t plo_pthread_tsd_base_address_offset =
5622061da546Spatrick         get_integer_value_for_key_name_from_json(
5623061da546Spatrick             "plo_pthread_tsd_base_address_offset", p);
5624061da546Spatrick     uint64_t plo_pthread_tsd_base_offset =
5625061da546Spatrick         get_integer_value_for_key_name_from_json("plo_pthread_tsd_base_offset",
5626061da546Spatrick                                                  p);
5627061da546Spatrick     uint64_t plo_pthread_tsd_entry_size =
5628061da546Spatrick         get_integer_value_for_key_name_from_json("plo_pthread_tsd_entry_size",
5629061da546Spatrick                                                  p);
5630061da546Spatrick     uint64_t dti_qos_class_index =
5631061da546Spatrick         get_integer_value_for_key_name_from_json("dti_qos_class_index", p);
5632061da546Spatrick 
5633061da546Spatrick     if (tid != INVALID_NUB_ADDRESS) {
5634061da546Spatrick       nub_addr_t pthread_t_value = DNBGetPThreadT(pid, tid);
5635061da546Spatrick 
5636061da546Spatrick       uint64_t tsd_address = INVALID_NUB_ADDRESS;
5637061da546Spatrick       if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS &&
5638061da546Spatrick           plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS &&
5639061da546Spatrick           plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS) {
5640061da546Spatrick         tsd_address = DNBGetTSDAddressForThread(
5641061da546Spatrick             pid, tid, plo_pthread_tsd_base_address_offset,
5642061da546Spatrick             plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
5643061da546Spatrick       }
5644061da546Spatrick 
5645061da546Spatrick       bool timed_out = false;
5646061da546Spatrick       Genealogy::ThreadActivitySP thread_activity_sp;
5647061da546Spatrick 
5648061da546Spatrick       // If the pthread_t value is invalid, or if we were able to fetch the
5649061da546Spatrick       // thread's TSD base
5650061da546Spatrick       // and got an invalid value back, then we have a thread in early startup
5651061da546Spatrick       // or shutdown and
5652061da546Spatrick       // it's possible that gathering the genealogy information for this thread
5653061da546Spatrick       // go badly.
5654061da546Spatrick       // Ideally fetching this info for a thread in these odd states shouldn't
5655061da546Spatrick       // matter - but
5656061da546Spatrick       // we've seen some problems with these new SPI and threads in edge-casey
5657061da546Spatrick       // states.
5658061da546Spatrick 
5659061da546Spatrick       double genealogy_fetch_time = 0;
5660061da546Spatrick       if (pthread_t_value != INVALID_NUB_ADDRESS &&
5661061da546Spatrick           tsd_address != INVALID_NUB_ADDRESS) {
5662061da546Spatrick         DNBTimer timer(false);
5663061da546Spatrick         thread_activity_sp = DNBGetGenealogyInfoForThread(pid, tid, timed_out);
5664061da546Spatrick         genealogy_fetch_time = timer.ElapsedMicroSeconds(false) / 1000000.0;
5665061da546Spatrick       }
5666061da546Spatrick 
5667061da546Spatrick       std::unordered_set<uint32_t>
5668061da546Spatrick           process_info_indexes; // an array of the process info #'s seen
5669061da546Spatrick 
5670061da546Spatrick       json << "{";
5671061da546Spatrick 
5672061da546Spatrick       bool need_to_print_comma = false;
5673061da546Spatrick 
5674061da546Spatrick       if (thread_activity_sp && !timed_out) {
5675061da546Spatrick         const Genealogy::Activity *activity =
5676061da546Spatrick             &thread_activity_sp->current_activity;
5677061da546Spatrick         bool need_vouchers_comma_sep = false;
5678061da546Spatrick         json << "\"activity_query_timed_out\":false,";
5679061da546Spatrick         if (genealogy_fetch_time != 0) {
5680061da546Spatrick           //  If we append the floating point value with << we'll get it in
5681061da546Spatrick           //  scientific
5682061da546Spatrick           //  notation.
5683061da546Spatrick           char floating_point_ascii_buffer[64];
5684061da546Spatrick           floating_point_ascii_buffer[0] = '\0';
5685061da546Spatrick           snprintf(floating_point_ascii_buffer,
5686061da546Spatrick                    sizeof(floating_point_ascii_buffer), "%f",
5687061da546Spatrick                    genealogy_fetch_time);
5688061da546Spatrick           if (strlen(floating_point_ascii_buffer) > 0) {
5689061da546Spatrick             if (need_to_print_comma)
5690061da546Spatrick               json << ",";
5691061da546Spatrick             need_to_print_comma = true;
5692061da546Spatrick             json << "\"activity_query_duration\":"
5693061da546Spatrick                  << floating_point_ascii_buffer;
5694061da546Spatrick           }
5695061da546Spatrick         }
5696061da546Spatrick         if (activity->activity_id != 0) {
5697061da546Spatrick           if (need_to_print_comma)
5698061da546Spatrick             json << ",";
5699061da546Spatrick           need_to_print_comma = true;
5700061da546Spatrick           need_vouchers_comma_sep = true;
5701061da546Spatrick           json << "\"activity\":{";
5702061da546Spatrick           json << "\"start\":" << activity->activity_start << ",";
5703061da546Spatrick           json << "\"id\":" << activity->activity_id << ",";
5704061da546Spatrick           json << "\"parent_id\":" << activity->parent_id << ",";
5705061da546Spatrick           json << "\"name\":\""
5706061da546Spatrick                << json_string_quote_metachars(activity->activity_name) << "\",";
5707061da546Spatrick           json << "\"reason\":\""
5708061da546Spatrick                << json_string_quote_metachars(activity->reason) << "\"";
5709061da546Spatrick           json << "}";
5710061da546Spatrick         }
5711061da546Spatrick         if (thread_activity_sp->messages.size() > 0) {
5712061da546Spatrick           need_to_print_comma = true;
5713061da546Spatrick           if (need_vouchers_comma_sep)
5714061da546Spatrick             json << ",";
5715061da546Spatrick           need_vouchers_comma_sep = true;
5716061da546Spatrick           json << "\"trace_messages\":[";
5717061da546Spatrick           bool printed_one_message = false;
5718061da546Spatrick           for (auto iter = thread_activity_sp->messages.begin();
5719061da546Spatrick                iter != thread_activity_sp->messages.end(); ++iter) {
5720061da546Spatrick             if (printed_one_message)
5721061da546Spatrick               json << ",";
5722061da546Spatrick             else
5723061da546Spatrick               printed_one_message = true;
5724061da546Spatrick             json << "{";
5725061da546Spatrick             json << "\"timestamp\":" << iter->timestamp << ",";
5726061da546Spatrick             json << "\"activity_id\":" << iter->activity_id << ",";
5727061da546Spatrick             json << "\"trace_id\":" << iter->trace_id << ",";
5728061da546Spatrick             json << "\"thread\":" << iter->thread << ",";
5729061da546Spatrick             json << "\"type\":" << (int)iter->type << ",";
5730061da546Spatrick             json << "\"process_info_index\":" << iter->process_info_index
5731061da546Spatrick                  << ",";
5732061da546Spatrick             process_info_indexes.insert(iter->process_info_index);
5733061da546Spatrick             json << "\"message\":\""
5734061da546Spatrick                  << json_string_quote_metachars(iter->message) << "\"";
5735061da546Spatrick             json << "}";
5736061da546Spatrick           }
5737061da546Spatrick           json << "]";
5738061da546Spatrick         }
5739061da546Spatrick         if (thread_activity_sp->breadcrumbs.size() == 1) {
5740061da546Spatrick           need_to_print_comma = true;
5741061da546Spatrick           if (need_vouchers_comma_sep)
5742061da546Spatrick             json << ",";
5743061da546Spatrick           need_vouchers_comma_sep = true;
5744061da546Spatrick           json << "\"breadcrumb\":{";
5745061da546Spatrick           for (auto iter = thread_activity_sp->breadcrumbs.begin();
5746061da546Spatrick                iter != thread_activity_sp->breadcrumbs.end(); ++iter) {
5747061da546Spatrick             json << "\"breadcrumb_id\":" << iter->breadcrumb_id << ",";
5748061da546Spatrick             json << "\"activity_id\":" << iter->activity_id << ",";
5749061da546Spatrick             json << "\"timestamp\":" << iter->timestamp << ",";
5750061da546Spatrick             json << "\"name\":\"" << json_string_quote_metachars(iter->name)
5751061da546Spatrick                  << "\"";
5752061da546Spatrick           }
5753061da546Spatrick           json << "}";
5754061da546Spatrick         }
5755061da546Spatrick         if (process_info_indexes.size() > 0) {
5756061da546Spatrick           need_to_print_comma = true;
5757061da546Spatrick           if (need_vouchers_comma_sep)
5758061da546Spatrick             json << ",";
5759061da546Spatrick           need_vouchers_comma_sep = true;
5760061da546Spatrick           bool printed_one_process_info = false;
5761061da546Spatrick           for (auto iter = process_info_indexes.begin();
5762061da546Spatrick                iter != process_info_indexes.end(); ++iter) {
5763061da546Spatrick             if (printed_one_process_info)
5764061da546Spatrick               json << ",";
5765061da546Spatrick             Genealogy::ProcessExecutableInfoSP image_info_sp;
5766061da546Spatrick             uint32_t idx = *iter;
5767061da546Spatrick             image_info_sp = DNBGetGenealogyImageInfo(pid, idx);
5768061da546Spatrick             if (image_info_sp) {
5769061da546Spatrick               if (!printed_one_process_info) {
5770061da546Spatrick                 json << "\"process_infos\":[";
5771061da546Spatrick                 printed_one_process_info = true;
5772061da546Spatrick               }
5773061da546Spatrick 
5774061da546Spatrick               json << "{";
5775061da546Spatrick               char uuid_buf[37];
5776061da546Spatrick               uuid_unparse_upper(image_info_sp->image_uuid, uuid_buf);
5777061da546Spatrick               json << "\"process_info_index\":" << idx << ",";
5778061da546Spatrick               json << "\"image_path\":\""
5779061da546Spatrick                    << json_string_quote_metachars(image_info_sp->image_path)
5780061da546Spatrick                    << "\",";
5781061da546Spatrick               json << "\"image_uuid\":\"" << uuid_buf << "\"";
5782061da546Spatrick               json << "}";
5783061da546Spatrick             }
5784061da546Spatrick           }
5785061da546Spatrick           if (printed_one_process_info)
5786061da546Spatrick             json << "]";
5787061da546Spatrick         }
5788061da546Spatrick       } else {
5789061da546Spatrick         if (timed_out) {
5790061da546Spatrick           if (need_to_print_comma)
5791061da546Spatrick             json << ",";
5792061da546Spatrick           need_to_print_comma = true;
5793061da546Spatrick           json << "\"activity_query_timed_out\":true";
5794061da546Spatrick           if (genealogy_fetch_time != 0) {
5795061da546Spatrick             //  If we append the floating point value with << we'll get it in
5796061da546Spatrick             //  scientific
5797061da546Spatrick             //  notation.
5798061da546Spatrick             char floating_point_ascii_buffer[64];
5799061da546Spatrick             floating_point_ascii_buffer[0] = '\0';
5800061da546Spatrick             snprintf(floating_point_ascii_buffer,
5801061da546Spatrick                      sizeof(floating_point_ascii_buffer), "%f",
5802061da546Spatrick                      genealogy_fetch_time);
5803061da546Spatrick             if (strlen(floating_point_ascii_buffer) > 0) {
5804061da546Spatrick               json << ",";
5805061da546Spatrick               json << "\"activity_query_duration\":"
5806061da546Spatrick                    << floating_point_ascii_buffer;
5807061da546Spatrick             }
5808061da546Spatrick           }
5809061da546Spatrick         }
5810061da546Spatrick       }
5811061da546Spatrick 
5812061da546Spatrick       if (tsd_address != INVALID_NUB_ADDRESS) {
5813061da546Spatrick         if (need_to_print_comma)
5814061da546Spatrick           json << ",";
5815061da546Spatrick         need_to_print_comma = true;
5816061da546Spatrick         json << "\"tsd_address\":" << tsd_address;
5817061da546Spatrick 
5818061da546Spatrick         if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX) {
5819061da546Spatrick           ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread(
5820061da546Spatrick               pid, tid, tsd_address, dti_qos_class_index);
5821061da546Spatrick           if (requested_qos.IsValid()) {
5822061da546Spatrick             if (need_to_print_comma)
5823061da546Spatrick               json << ",";
5824061da546Spatrick             need_to_print_comma = true;
5825061da546Spatrick             json << "\"requested_qos\":{";
5826061da546Spatrick             json << "\"enum_value\":" << requested_qos.enum_value << ",";
5827061da546Spatrick             json << "\"constant_name\":\""
5828061da546Spatrick                  << json_string_quote_metachars(requested_qos.constant_name)
5829061da546Spatrick                  << "\",";
5830061da546Spatrick             json << "\"printable_name\":\""
5831061da546Spatrick                  << json_string_quote_metachars(requested_qos.printable_name)
5832061da546Spatrick                  << "\"";
5833061da546Spatrick             json << "}";
5834061da546Spatrick           }
5835061da546Spatrick         }
5836061da546Spatrick       }
5837061da546Spatrick 
5838061da546Spatrick       if (pthread_t_value != INVALID_NUB_ADDRESS) {
5839061da546Spatrick         if (need_to_print_comma)
5840061da546Spatrick           json << ",";
5841061da546Spatrick         need_to_print_comma = true;
5842061da546Spatrick         json << "\"pthread_t\":" << pthread_t_value;
5843061da546Spatrick       }
5844061da546Spatrick 
5845061da546Spatrick       nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT(pid, tid);
5846061da546Spatrick       if (dispatch_queue_t_value != INVALID_NUB_ADDRESS) {
5847061da546Spatrick         if (need_to_print_comma)
5848061da546Spatrick           json << ",";
5849061da546Spatrick         need_to_print_comma = true;
5850061da546Spatrick         json << "\"dispatch_queue_t\":" << dispatch_queue_t_value;
5851061da546Spatrick       }
5852061da546Spatrick 
5853061da546Spatrick       json << "}";
5854061da546Spatrick       std::string json_quoted = binary_encode_string(json.str());
5855061da546Spatrick       return SendPacket(json_quoted);
5856061da546Spatrick     }
5857061da546Spatrick   }
5858061da546Spatrick   return SendPacket("OK");
5859061da546Spatrick }
5860061da546Spatrick 
5861061da546Spatrick //  This packet may be called in one of three ways:
5862061da546Spatrick //
5863061da546Spatrick //  jGetLoadedDynamicLibrariesInfos:{"image_count":40,"image_list_address":4295244704}
5864061da546Spatrick //      Look for an array of the old dyld_all_image_infos style of binary infos
5865061da546Spatrick //      at the image_list_address.
5866061da546Spatrick //      This an array of {void* load_addr, void* mod_date, void* pathname}
5867061da546Spatrick //
5868061da546Spatrick //  jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}
5869061da546Spatrick //      Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to
5870061da546Spatrick //      get a list of all the
5871061da546Spatrick //      libraries loaded
5872061da546Spatrick //
5873061da546Spatrick //  jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]}
5874061da546Spatrick //      Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to
5875061da546Spatrick //      get the information
5876061da546Spatrick //      about the libraries loaded at these addresses.
5877061da546Spatrick //
5878061da546Spatrick rnb_err_t
HandlePacket_jGetLoadedDynamicLibrariesInfos(const char * p)5879061da546Spatrick RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos(const char *p) {
5880061da546Spatrick   nub_process_t pid;
5881061da546Spatrick   // If we haven't run the process yet, return an error.
5882061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
5883061da546Spatrick     return SendPacket("E83");
5884061da546Spatrick   }
5885061da546Spatrick 
5886061da546Spatrick   pid = m_ctx.ProcessID();
5887061da546Spatrick 
5888061da546Spatrick   const char get_loaded_dynamic_libraries_infos_str[] = {
5889061da546Spatrick       "jGetLoadedDynamicLibrariesInfos:{"};
5890061da546Spatrick   if (strncmp(p, get_loaded_dynamic_libraries_infos_str,
5891061da546Spatrick               sizeof(get_loaded_dynamic_libraries_infos_str) - 1) == 0) {
5892061da546Spatrick     p += strlen(get_loaded_dynamic_libraries_infos_str);
5893061da546Spatrick 
5894061da546Spatrick     JSONGenerator::ObjectSP json_sp;
5895061da546Spatrick 
5896061da546Spatrick     std::vector<uint64_t> macho_addresses;
5897061da546Spatrick     bool fetch_all_solibs = false;
5898061da546Spatrick     if (get_boolean_value_for_key_name_from_json("fetch_all_solibs", p,
5899061da546Spatrick                                                  fetch_all_solibs) &&
5900061da546Spatrick         fetch_all_solibs) {
5901061da546Spatrick       json_sp = DNBGetAllLoadedLibrariesInfos(pid);
5902061da546Spatrick     } else if (get_array_of_ints_value_for_key_name_from_json(
5903061da546Spatrick                    "solib_addresses", p, macho_addresses)) {
5904061da546Spatrick       json_sp = DNBGetLibrariesInfoForAddresses(pid, macho_addresses);
5905061da546Spatrick     } else {
5906061da546Spatrick       nub_addr_t image_list_address =
5907061da546Spatrick           get_integer_value_for_key_name_from_json("image_list_address", p);
5908061da546Spatrick       nub_addr_t image_count =
5909061da546Spatrick           get_integer_value_for_key_name_from_json("image_count", p);
5910061da546Spatrick 
5911061da546Spatrick       if (image_list_address != INVALID_NUB_ADDRESS &&
5912061da546Spatrick           image_count != INVALID_NUB_ADDRESS) {
5913061da546Spatrick         json_sp = DNBGetLoadedDynamicLibrariesInfos(pid, image_list_address,
5914061da546Spatrick                                                     image_count);
5915061da546Spatrick       }
5916061da546Spatrick     }
5917061da546Spatrick 
5918061da546Spatrick     if (json_sp.get()) {
5919061da546Spatrick       std::ostringstream json_str;
5920*f6aab3d8Srobert       json_sp->DumpBinaryEscaped(json_str);
5921*f6aab3d8Srobert       json_sp->Clear();
5922061da546Spatrick       if (json_str.str().size() > 0) {
5923*f6aab3d8Srobert         return SendPacket(json_str.str());
5924061da546Spatrick       } else {
5925061da546Spatrick         SendPacket("E84");
5926061da546Spatrick       }
5927061da546Spatrick     }
5928061da546Spatrick   }
5929061da546Spatrick   return SendPacket("OK");
5930061da546Spatrick }
5931061da546Spatrick 
5932061da546Spatrick // This packet does not currently take any arguments.  So the behavior is
5933061da546Spatrick //    jGetSharedCacheInfo:{}
5934061da546Spatrick //         send information about the inferior's shared cache
5935061da546Spatrick //    jGetSharedCacheInfo:
5936061da546Spatrick //         send "OK" to indicate that this packet is supported
HandlePacket_jGetSharedCacheInfo(const char * p)5937061da546Spatrick rnb_err_t RNBRemote::HandlePacket_jGetSharedCacheInfo(const char *p) {
5938061da546Spatrick   nub_process_t pid;
5939061da546Spatrick   // If we haven't run the process yet, return an error.
5940061da546Spatrick   if (!m_ctx.HasValidProcessID()) {
5941061da546Spatrick     return SendPacket("E85");
5942061da546Spatrick   }
5943061da546Spatrick 
5944061da546Spatrick   pid = m_ctx.ProcessID();
5945061da546Spatrick 
5946061da546Spatrick   const char get_shared_cache_info_str[] = {"jGetSharedCacheInfo:{"};
5947061da546Spatrick   if (strncmp(p, get_shared_cache_info_str,
5948061da546Spatrick               sizeof(get_shared_cache_info_str) - 1) == 0) {
5949061da546Spatrick     JSONGenerator::ObjectSP json_sp = DNBGetSharedCacheInfo(pid);
5950061da546Spatrick 
5951061da546Spatrick     if (json_sp.get()) {
5952061da546Spatrick       std::ostringstream json_str;
5953*f6aab3d8Srobert       json_sp->DumpBinaryEscaped(json_str);
5954*f6aab3d8Srobert       json_sp->Clear();
5955061da546Spatrick       if (json_str.str().size() > 0) {
5956*f6aab3d8Srobert         return SendPacket(json_str.str());
5957061da546Spatrick       } else {
5958061da546Spatrick         SendPacket("E86");
5959061da546Spatrick       }
5960061da546Spatrick     }
5961061da546Spatrick   }
5962061da546Spatrick   return SendPacket("OK");
5963061da546Spatrick }
5964061da546Spatrick 
MachHeaderIsMainExecutable(nub_process_t pid,uint32_t addr_size,nub_addr_t mach_header_addr,mach_header & mh)5965061da546Spatrick static bool MachHeaderIsMainExecutable(nub_process_t pid, uint32_t addr_size,
5966061da546Spatrick                                        nub_addr_t mach_header_addr,
5967061da546Spatrick                                        mach_header &mh) {
5968061da546Spatrick   DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, "
5969061da546Spatrick                                  "addr_size = %u, mach_header_addr = "
5970061da546Spatrick                                  "0x%16.16llx)",
5971061da546Spatrick                    pid, addr_size, mach_header_addr);
5972061da546Spatrick   const nub_size_t bytes_read =
5973061da546Spatrick       DNBProcessMemoryRead(pid, mach_header_addr, sizeof(mh), &mh);
5974061da546Spatrick   if (bytes_read == sizeof(mh)) {
5975061da546Spatrick     DNBLogThreadedIf(
5976061da546Spatrick         LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = "
5977061da546Spatrick                       "%u, mach_header_addr = 0x%16.16llx): mh = {\n  magic = "
5978061da546Spatrick                       "0x%8.8x\n  cpu = 0x%8.8x\n  sub = 0x%8.8x\n  filetype = "
5979061da546Spatrick                       "%u\n  ncmds = %u\n  sizeofcmds = 0x%8.8x\n  flags = "
5980061da546Spatrick                       "0x%8.8x }",
5981061da546Spatrick         pid, addr_size, mach_header_addr, mh.magic, mh.cputype, mh.cpusubtype,
5982061da546Spatrick         mh.filetype, mh.ncmds, mh.sizeofcmds, mh.flags);
5983061da546Spatrick     if ((addr_size == 4 && mh.magic == MH_MAGIC) ||
5984061da546Spatrick         (addr_size == 8 && mh.magic == MH_MAGIC_64)) {
5985061da546Spatrick       if (mh.filetype == MH_EXECUTE) {
5986061da546Spatrick         DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = "
5987061da546Spatrick                                        "%u, addr_size = %u, mach_header_addr = "
5988061da546Spatrick                                        "0x%16.16llx) -> this is the "
5989061da546Spatrick                                        "executable!!!",
5990061da546Spatrick                          pid, addr_size, mach_header_addr);
5991061da546Spatrick         return true;
5992061da546Spatrick       }
5993061da546Spatrick     }
5994061da546Spatrick   }
5995061da546Spatrick   return false;
5996061da546Spatrick }
5997061da546Spatrick 
GetMachHeaderForMainExecutable(const nub_process_t pid,const uint32_t addr_size,mach_header & mh)5998061da546Spatrick static nub_addr_t GetMachHeaderForMainExecutable(const nub_process_t pid,
5999061da546Spatrick                                                  const uint32_t addr_size,
6000061da546Spatrick                                                  mach_header &mh) {
6001061da546Spatrick   struct AllImageInfos {
6002061da546Spatrick     uint32_t version;
6003061da546Spatrick     uint32_t dylib_info_count;
6004061da546Spatrick     uint64_t dylib_info_addr;
6005061da546Spatrick   };
6006061da546Spatrick 
6007061da546Spatrick   uint64_t mach_header_addr = 0;
6008061da546Spatrick 
6009061da546Spatrick   const nub_addr_t shlib_addr = DNBProcessGetSharedLibraryInfoAddress(pid);
6010061da546Spatrick   uint8_t bytes[256];
6011061da546Spatrick   nub_size_t bytes_read = 0;
6012061da546Spatrick   DNBDataRef data(bytes, sizeof(bytes), false);
6013061da546Spatrick   DNBDataRef::offset_t offset = 0;
6014061da546Spatrick   data.SetPointerSize(addr_size);
6015061da546Spatrick 
6016061da546Spatrick   // When we are sitting at __dyld_start, the kernel has placed the
6017061da546Spatrick   // address of the mach header of the main executable on the stack. If we
6018061da546Spatrick   // read the SP and dereference a pointer, we might find the mach header
6019061da546Spatrick   // for the executable. We also just make sure there is only 1 thread
6020061da546Spatrick   // since if we are at __dyld_start we shouldn't have multiple threads.
6021061da546Spatrick   if (DNBProcessGetNumThreads(pid) == 1) {
6022061da546Spatrick     nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, 0);
6023061da546Spatrick     if (tid != INVALID_NUB_THREAD) {
6024061da546Spatrick       DNBRegisterValue sp_value;
6025061da546Spatrick       if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC,
6026061da546Spatrick                                         GENERIC_REGNUM_SP, &sp_value)) {
6027061da546Spatrick         uint64_t sp =
6028061da546Spatrick             addr_size == 8 ? sp_value.value.uint64 : sp_value.value.uint32;
6029061da546Spatrick         bytes_read = DNBProcessMemoryRead(pid, sp, addr_size, bytes);
6030061da546Spatrick         if (bytes_read == addr_size) {
6031061da546Spatrick           offset = 0;
6032061da546Spatrick           mach_header_addr = data.GetPointer(&offset);
6033061da546Spatrick           if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
6034061da546Spatrick             return mach_header_addr;
6035061da546Spatrick         }
6036061da546Spatrick       }
6037061da546Spatrick     }
6038061da546Spatrick   }
6039061da546Spatrick 
6040061da546Spatrick   // Check the dyld_all_image_info structure for a list of mach header
6041061da546Spatrick   // since it is a very easy thing to check
6042061da546Spatrick   if (shlib_addr != INVALID_NUB_ADDRESS) {
6043061da546Spatrick     bytes_read =
6044061da546Spatrick         DNBProcessMemoryRead(pid, shlib_addr, sizeof(AllImageInfos), bytes);
6045061da546Spatrick     if (bytes_read > 0) {
6046061da546Spatrick       AllImageInfos aii;
6047061da546Spatrick       offset = 0;
6048061da546Spatrick       aii.version = data.Get32(&offset);
6049061da546Spatrick       aii.dylib_info_count = data.Get32(&offset);
6050061da546Spatrick       if (aii.dylib_info_count > 0) {
6051061da546Spatrick         aii.dylib_info_addr = data.GetPointer(&offset);
6052061da546Spatrick         if (aii.dylib_info_addr != 0) {
6053061da546Spatrick           const size_t image_info_byte_size = 3 * addr_size;
6054061da546Spatrick           for (uint32_t i = 0; i < aii.dylib_info_count; ++i) {
6055061da546Spatrick             bytes_read = DNBProcessMemoryRead(pid, aii.dylib_info_addr +
6056061da546Spatrick                                                        i * image_info_byte_size,
6057061da546Spatrick                                               image_info_byte_size, bytes);
6058061da546Spatrick             if (bytes_read != image_info_byte_size)
6059061da546Spatrick               break;
6060061da546Spatrick             offset = 0;
6061061da546Spatrick             mach_header_addr = data.GetPointer(&offset);
6062061da546Spatrick             if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr,
6063061da546Spatrick                                            mh))
6064061da546Spatrick               return mach_header_addr;
6065061da546Spatrick           }
6066061da546Spatrick         }
6067061da546Spatrick       }
6068061da546Spatrick     }
6069061da546Spatrick   }
6070061da546Spatrick 
6071061da546Spatrick   // We failed to find the executable's mach header from the all image
6072061da546Spatrick   // infos and by dereferencing the stack pointer. Now we fall back to
6073061da546Spatrick   // enumerating the memory regions and looking for regions that are
6074061da546Spatrick   // executable.
6075061da546Spatrick   DNBRegionInfo region_info;
6076061da546Spatrick   mach_header_addr = 0;
6077061da546Spatrick   while (DNBProcessMemoryRegionInfo(pid, mach_header_addr, &region_info)) {
6078061da546Spatrick     if (region_info.size == 0)
6079061da546Spatrick       break;
6080061da546Spatrick 
6081061da546Spatrick     if (region_info.permissions & eMemoryPermissionsExecutable) {
6082061da546Spatrick       DNBLogThreadedIf(
6083061da546Spatrick           LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx) permissions = %c%c%c: "
6084061da546Spatrick                         "checking region for executable mach header",
6085061da546Spatrick           region_info.addr, region_info.addr + region_info.size,
6086061da546Spatrick           (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-',
6087061da546Spatrick           (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-',
6088061da546Spatrick           (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
6089061da546Spatrick       if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
6090061da546Spatrick         return mach_header_addr;
6091061da546Spatrick     } else {
6092061da546Spatrick       DNBLogThreadedIf(
6093061da546Spatrick           LOG_RNB_PROC,
6094061da546Spatrick           "[0x%16.16llx - 0x%16.16llx): permissions = %c%c%c: skipping region",
6095061da546Spatrick           region_info.addr, region_info.addr + region_info.size,
6096061da546Spatrick           (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-',
6097061da546Spatrick           (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-',
6098061da546Spatrick           (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
6099061da546Spatrick     }
6100061da546Spatrick     // Set the address to the next mapped region
6101061da546Spatrick     mach_header_addr = region_info.addr + region_info.size;
6102061da546Spatrick   }
6103061da546Spatrick   bzero(&mh, sizeof(mh));
6104061da546Spatrick   return INVALID_NUB_ADDRESS;
6105061da546Spatrick }
6106061da546Spatrick 
HandlePacket_qSymbol(const char * command)6107061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qSymbol(const char *command) {
6108061da546Spatrick   const char *p = command;
6109061da546Spatrick   p += strlen("qSymbol:");
6110061da546Spatrick   const char *sep = strchr(p, ':');
6111061da546Spatrick 
6112061da546Spatrick   std::string symbol_name;
6113061da546Spatrick   std::string symbol_value_str;
6114061da546Spatrick   // Extract the symbol value if there is one
6115061da546Spatrick   if (sep > p)
6116061da546Spatrick     symbol_value_str.assign(p, sep - p);
6117061da546Spatrick   p = sep + 1;
6118061da546Spatrick 
6119061da546Spatrick   if (*p) {
6120061da546Spatrick     // We have a symbol name
6121061da546Spatrick     symbol_name = decode_hex_ascii_string(p);
6122061da546Spatrick     if (!symbol_value_str.empty()) {
6123061da546Spatrick       nub_addr_t symbol_value = decode_uint64(symbol_value_str.c_str(), 16);
6124061da546Spatrick       if (symbol_name == "dispatch_queue_offsets")
6125061da546Spatrick         m_dispatch_queue_offsets_addr = symbol_value;
6126061da546Spatrick     }
6127061da546Spatrick     ++m_qSymbol_index;
6128061da546Spatrick   } else {
6129061da546Spatrick     // No symbol name, set our symbol index to zero so we can
6130061da546Spatrick     // read any symbols that we need
6131061da546Spatrick     m_qSymbol_index = 0;
6132061da546Spatrick   }
6133061da546Spatrick 
6134061da546Spatrick   symbol_name.clear();
6135061da546Spatrick 
6136061da546Spatrick   if (m_qSymbol_index == 0) {
6137061da546Spatrick     if (m_dispatch_queue_offsets_addr == INVALID_NUB_ADDRESS)
6138061da546Spatrick       symbol_name = "dispatch_queue_offsets";
6139061da546Spatrick     else
6140061da546Spatrick       ++m_qSymbol_index;
6141061da546Spatrick   }
6142061da546Spatrick 
6143061da546Spatrick   //    // Lookup next symbol when we have one...
6144061da546Spatrick   //    if (m_qSymbol_index == 1)
6145061da546Spatrick   //    {
6146061da546Spatrick   //    }
6147061da546Spatrick 
6148061da546Spatrick   if (symbol_name.empty()) {
6149061da546Spatrick     // Done with symbol lookups
6150061da546Spatrick     return SendPacket("OK");
6151061da546Spatrick   } else {
6152061da546Spatrick     std::ostringstream reply;
6153061da546Spatrick     reply << "qSymbol:";
6154061da546Spatrick     for (size_t i = 0; i < symbol_name.size(); ++i)
6155061da546Spatrick       reply << RAWHEX8(symbol_name[i]);
6156*f6aab3d8Srobert     return SendPacket(reply.str());
6157061da546Spatrick   }
6158061da546Spatrick }
6159061da546Spatrick 
6160061da546Spatrick // Note that all numeric values returned by qProcessInfo are hex encoded,
6161061da546Spatrick // including the pid and the cpu type.
6162061da546Spatrick 
HandlePacket_qProcessInfo(const char * p)6163061da546Spatrick rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) {
6164061da546Spatrick   nub_process_t pid;
6165061da546Spatrick   std::ostringstream rep;
6166061da546Spatrick 
6167061da546Spatrick   // If we haven't run the process yet, return an error.
6168061da546Spatrick   if (!m_ctx.HasValidProcessID())
6169061da546Spatrick     return SendPacket("E68");
6170061da546Spatrick 
6171061da546Spatrick   pid = m_ctx.ProcessID();
6172061da546Spatrick 
6173061da546Spatrick   rep << "pid:" << std::hex << pid << ';';
6174061da546Spatrick 
6175061da546Spatrick   int procpid_mib[4];
6176061da546Spatrick   procpid_mib[0] = CTL_KERN;
6177061da546Spatrick   procpid_mib[1] = KERN_PROC;
6178061da546Spatrick   procpid_mib[2] = KERN_PROC_PID;
6179061da546Spatrick   procpid_mib[3] = pid;
6180061da546Spatrick   struct kinfo_proc proc_kinfo;
6181061da546Spatrick   size_t proc_kinfo_size = sizeof(struct kinfo_proc);
6182061da546Spatrick 
6183061da546Spatrick   if (::sysctl(procpid_mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
6184061da546Spatrick     if (proc_kinfo_size > 0) {
6185061da546Spatrick       rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ';';
6186061da546Spatrick       rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid
6187061da546Spatrick           << ';';
6188061da546Spatrick       rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid
6189061da546Spatrick           << ';';
6190061da546Spatrick       rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid
6191061da546Spatrick           << ';';
6192061da546Spatrick       if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
6193061da546Spatrick         rep << "effective-gid:" << std::hex
6194061da546Spatrick             << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ';';
6195061da546Spatrick     }
6196061da546Spatrick   }
6197061da546Spatrick 
6198061da546Spatrick   cpu_type_t cputype = DNBProcessGetCPUType(pid);
6199061da546Spatrick   if (cputype == 0) {
6200061da546Spatrick     DNBLog("Unable to get the process cpu_type, making a best guess.");
6201061da546Spatrick     cputype = best_guess_cpu_type();
6202061da546Spatrick   }
6203061da546Spatrick 
6204061da546Spatrick   uint32_t addr_size = 0;
6205061da546Spatrick   if (cputype != 0) {
6206061da546Spatrick     rep << "cputype:" << std::hex << cputype << ";";
6207061da546Spatrick     if (cputype & CPU_ARCH_ABI64)
6208061da546Spatrick       addr_size = 8;
6209061da546Spatrick     else
6210061da546Spatrick       addr_size = 4;
6211061da546Spatrick   }
6212061da546Spatrick 
6213061da546Spatrick   bool host_cpu_is_64bit = false;
6214061da546Spatrick   uint32_t is64bit_capable;
6215061da546Spatrick   size_t is64bit_capable_len = sizeof(is64bit_capable);
6216061da546Spatrick   if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable,
6217061da546Spatrick                    &is64bit_capable_len, NULL, 0) == 0)
6218061da546Spatrick     host_cpu_is_64bit = is64bit_capable != 0;
6219061da546Spatrick 
6220061da546Spatrick   uint32_t cpusubtype;
6221061da546Spatrick   size_t cpusubtype_len = sizeof(cpusubtype);
6222061da546Spatrick   if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &cpusubtype_len, NULL, 0) ==
6223061da546Spatrick       0) {
6224061da546Spatrick     // If a process is CPU_TYPE_X86, then ignore the cpusubtype that we detected
6225061da546Spatrick     // from the host and use CPU_SUBTYPE_I386_ALL because we don't want the
6226061da546Spatrick     // CPU_SUBTYPE_X86_ARCH1 or CPU_SUBTYPE_X86_64_H to be used as the cpu
6227061da546Spatrick     // subtype
6228061da546Spatrick     // for i386...
6229061da546Spatrick     if (host_cpu_is_64bit) {
6230061da546Spatrick       if (cputype == CPU_TYPE_X86) {
6231061da546Spatrick         cpusubtype = 3; // CPU_SUBTYPE_I386_ALL
6232061da546Spatrick       } else if (cputype == CPU_TYPE_ARM) {
6233061da546Spatrick         // We can query a process' cputype but we cannot query a process'
6234061da546Spatrick         // cpusubtype.
6235061da546Spatrick         // If the process has cputype CPU_TYPE_ARM, then it is an armv7 (32-bit
6236061da546Spatrick         // process) and we
6237061da546Spatrick         // need to override the host cpusubtype (which is in the
6238061da546Spatrick         // CPU_SUBTYPE_ARM64 subtype namespace)
6239061da546Spatrick         // with a reasonable CPU_SUBTYPE_ARMV7 subtype.
6240061da546Spatrick         cpusubtype = 12; // CPU_SUBTYPE_ARM_V7K
6241061da546Spatrick       }
6242061da546Spatrick     }
6243061da546Spatrick #if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
6244061da546Spatrick     // on arm64_32 devices, the machine's native cpu type is
6245061da546Spatrick     // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e.
6246061da546Spatrick     // But we change the cputype to CPU_TYPE_ARM64_32 because
6247061da546Spatrick     // the user processes are all ILP32 processes today.
6248061da546Spatrick     // We also need to rewrite the cpusubtype so we vend
6249061da546Spatrick     // a valid cputype + cpusubtype combination.
6250061da546Spatrick     if (cputype == CPU_TYPE_ARM64_32 && cpusubtype == 2)
6251061da546Spatrick       cpusubtype = CPU_SUBTYPE_ARM64_32_V8;
6252061da546Spatrick #endif
6253061da546Spatrick 
6254061da546Spatrick     rep << "cpusubtype:" << std::hex << cpusubtype << ';';
6255061da546Spatrick   }
6256061da546Spatrick 
6257061da546Spatrick   bool os_handled = false;
6258061da546Spatrick   if (addr_size > 0) {
6259061da546Spatrick     rep << "ptrsize:" << std::dec << addr_size << ';';
6260061da546Spatrick 
6261dda28197Spatrick #if defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1
6262061da546Spatrick     // Try and get the OS type by looking at the load commands in the main
6263061da546Spatrick     // executable and looking for a LC_VERSION_MIN load command. This is the
6264061da546Spatrick     // most reliable way to determine the "ostype" value when on desktop.
6265061da546Spatrick 
6266061da546Spatrick     mach_header mh;
6267061da546Spatrick     nub_addr_t exe_mach_header_addr =
6268061da546Spatrick         GetMachHeaderForMainExecutable(pid, addr_size, mh);
6269061da546Spatrick     if (exe_mach_header_addr != INVALID_NUB_ADDRESS) {
6270061da546Spatrick       uint64_t load_command_addr =
6271061da546Spatrick           exe_mach_header_addr +
6272061da546Spatrick           ((addr_size == 8) ? sizeof(mach_header_64) : sizeof(mach_header));
6273061da546Spatrick       load_command lc;
6274061da546Spatrick       for (uint32_t i = 0; i < mh.ncmds && !os_handled; ++i) {
6275061da546Spatrick         const nub_size_t bytes_read =
6276061da546Spatrick             DNBProcessMemoryRead(pid, load_command_addr, sizeof(lc), &lc);
6277061da546Spatrick         (void)bytes_read;
6278061da546Spatrick 
6279be691f3bSpatrick         bool is_executable = true;
6280061da546Spatrick         uint32_t major_version, minor_version, patch_version;
6281*f6aab3d8Srobert         std::optional<std::string> platform =
6282be691f3bSpatrick             DNBGetDeploymentInfo(pid, is_executable, lc, load_command_addr,
6283be691f3bSpatrick                                  major_version, minor_version, patch_version);
6284061da546Spatrick         if (platform) {
6285061da546Spatrick           os_handled = true;
6286*f6aab3d8Srobert           rep << "ostype:" << *platform << ";";
6287061da546Spatrick           break;
6288061da546Spatrick         }
6289061da546Spatrick         load_command_addr = load_command_addr + lc.cmdsize;
6290061da546Spatrick       }
6291061da546Spatrick     }
6292dda28197Spatrick #endif // TARGET_OS_OSX
6293061da546Spatrick   }
6294061da546Spatrick 
6295061da546Spatrick   // If we weren't able to find the OS in a LC_VERSION_MIN load command, try
6296061da546Spatrick   // to set it correctly by using the cpu type and other tricks
6297061da546Spatrick   if (!os_handled) {
6298061da546Spatrick     // The OS in the triple should be "ios" or "macosx" which doesn't match our
6299061da546Spatrick     // "Darwin" which gets returned from "kern.ostype", so we need to hardcode
6300061da546Spatrick     // this for now.
6301061da546Spatrick     if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64
6302061da546Spatrick         || cputype == CPU_TYPE_ARM64_32) {
6303061da546Spatrick #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
6304061da546Spatrick       rep << "ostype:tvos;";
6305061da546Spatrick #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
6306061da546Spatrick       rep << "ostype:watchos;";
6307061da546Spatrick #elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
6308061da546Spatrick       rep << "ostype:bridgeos;";
6309dda28197Spatrick #elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1
6310dda28197Spatrick       rep << "ostype:macosx;";
6311061da546Spatrick #else
6312061da546Spatrick       rep << "ostype:ios;";
6313061da546Spatrick #endif
6314061da546Spatrick     } else {
6315061da546Spatrick       bool is_ios_simulator = false;
6316061da546Spatrick       if (cputype == CPU_TYPE_X86 || cputype == CPU_TYPE_X86_64) {
6317061da546Spatrick         // Check for iOS simulator binaries by getting the process argument
6318061da546Spatrick         // and environment and checking for SIMULATOR_UDID in the environment
6319061da546Spatrick         int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2, (int)pid};
6320061da546Spatrick 
6321061da546Spatrick         uint8_t arg_data[8192];
6322061da546Spatrick         size_t arg_data_size = sizeof(arg_data);
6323061da546Spatrick         if (::sysctl(proc_args_mib, 3, arg_data, &arg_data_size, NULL, 0) ==
6324061da546Spatrick             0) {
6325061da546Spatrick           DNBDataRef data(arg_data, arg_data_size, false);
6326061da546Spatrick           DNBDataRef::offset_t offset = 0;
6327061da546Spatrick           uint32_t argc = data.Get32(&offset);
6328061da546Spatrick           const char *cstr;
6329061da546Spatrick 
6330061da546Spatrick           cstr = data.GetCStr(&offset);
6331061da546Spatrick           if (cstr) {
6332061da546Spatrick             // Skip NULLs
6333061da546Spatrick             while (true) {
6334061da546Spatrick               const char *p = data.PeekCStr(offset);
6335061da546Spatrick               if ((p == NULL) || (*p != '\0'))
6336061da546Spatrick                 break;
6337061da546Spatrick               ++offset;
6338061da546Spatrick             }
6339061da546Spatrick             // Now skip all arguments
6340061da546Spatrick             for (uint32_t i = 0; i < argc; ++i) {
6341061da546Spatrick               data.GetCStr(&offset);
6342061da546Spatrick             }
6343061da546Spatrick 
6344061da546Spatrick             // Now iterate across all environment variables
6345061da546Spatrick             while ((cstr = data.GetCStr(&offset))) {
6346061da546Spatrick               if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) ==
6347061da546Spatrick                   0) {
6348061da546Spatrick                 is_ios_simulator = true;
6349061da546Spatrick                 break;
6350061da546Spatrick               }
6351061da546Spatrick               if (cstr[0] == '\0')
6352061da546Spatrick                 break;
6353061da546Spatrick             }
6354061da546Spatrick           }
6355061da546Spatrick         }
6356061da546Spatrick       }
6357061da546Spatrick       if (is_ios_simulator) {
6358061da546Spatrick #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
6359061da546Spatrick         rep << "ostype:tvos;";
6360061da546Spatrick #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
6361061da546Spatrick         rep << "ostype:watchos;";
6362061da546Spatrick #elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
6363061da546Spatrick         rep << "ostype:bridgeos;";
6364061da546Spatrick #else
6365061da546Spatrick         rep << "ostype:ios;";
6366061da546Spatrick #endif
6367061da546Spatrick       } else {
6368061da546Spatrick         rep << "ostype:macosx;";
6369061da546Spatrick       }
6370061da546Spatrick     }
6371061da546Spatrick   }
6372061da546Spatrick 
6373061da546Spatrick   rep << "vendor:apple;";
6374061da546Spatrick 
6375061da546Spatrick #if defined(__LITTLE_ENDIAN__)
6376061da546Spatrick   rep << "endian:little;";
6377061da546Spatrick #elif defined(__BIG_ENDIAN__)
6378061da546Spatrick   rep << "endian:big;";
6379061da546Spatrick #elif defined(__PDP_ENDIAN__)
6380061da546Spatrick   rep << "endian:pdp;";
6381061da546Spatrick #endif
6382061da546Spatrick 
6383061da546Spatrick   if (addr_size == 0) {
6384061da546Spatrick #if (defined(__x86_64__) || defined(__i386__)) && defined(x86_THREAD_STATE)
6385061da546Spatrick     nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid);
6386061da546Spatrick     kern_return_t kr;
6387061da546Spatrick     x86_thread_state_t gp_regs;
6388061da546Spatrick     mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT;
6389061da546Spatrick     kr = thread_get_state(static_cast<thread_act_t>(thread), x86_THREAD_STATE,
6390061da546Spatrick                           (thread_state_t)&gp_regs, &gp_count);
6391061da546Spatrick     if (kr == KERN_SUCCESS) {
6392061da546Spatrick       if (gp_regs.tsh.flavor == x86_THREAD_STATE64)
6393061da546Spatrick         rep << "ptrsize:8;";
6394061da546Spatrick       else
6395061da546Spatrick         rep << "ptrsize:4;";
6396061da546Spatrick     }
6397061da546Spatrick #elif defined(__arm__)
6398061da546Spatrick     rep << "ptrsize:4;";
6399061da546Spatrick #elif (defined(__arm64__) || defined(__aarch64__)) &&                          \
6400061da546Spatrick     defined(ARM_UNIFIED_THREAD_STATE)
6401061da546Spatrick     nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid);
6402061da546Spatrick     kern_return_t kr;
6403061da546Spatrick     arm_unified_thread_state_t gp_regs;
6404061da546Spatrick     mach_msg_type_number_t gp_count = ARM_UNIFIED_THREAD_STATE_COUNT;
6405061da546Spatrick     kr = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE,
6406061da546Spatrick                           (thread_state_t)&gp_regs, &gp_count);
6407061da546Spatrick     if (kr == KERN_SUCCESS) {
6408061da546Spatrick       if (gp_regs.ash.flavor == ARM_THREAD_STATE64)
6409061da546Spatrick         rep << "ptrsize:8;";
6410061da546Spatrick       else
6411061da546Spatrick         rep << "ptrsize:4;";
6412061da546Spatrick     }
6413061da546Spatrick #endif
6414061da546Spatrick   }
6415061da546Spatrick 
6416061da546Spatrick   return SendPacket(rep.str());
6417061da546Spatrick }
6418061da546Spatrick 
GetDispatchQueueOffsets()6419061da546Spatrick const RNBRemote::DispatchQueueOffsets *RNBRemote::GetDispatchQueueOffsets() {
6420061da546Spatrick   if (!m_dispatch_queue_offsets.IsValid() &&
6421061da546Spatrick       m_dispatch_queue_offsets_addr != INVALID_NUB_ADDRESS &&
6422061da546Spatrick       m_ctx.HasValidProcessID()) {
6423061da546Spatrick     nub_process_t pid = m_ctx.ProcessID();
6424061da546Spatrick     nub_size_t bytes_read = DNBProcessMemoryRead(
6425061da546Spatrick         pid, m_dispatch_queue_offsets_addr, sizeof(m_dispatch_queue_offsets),
6426061da546Spatrick         &m_dispatch_queue_offsets);
6427061da546Spatrick     if (bytes_read != sizeof(m_dispatch_queue_offsets))
6428061da546Spatrick       m_dispatch_queue_offsets.Clear();
6429061da546Spatrick   }
6430061da546Spatrick 
6431061da546Spatrick   if (m_dispatch_queue_offsets.IsValid())
6432061da546Spatrick     return &m_dispatch_queue_offsets;
6433061da546Spatrick   else
6434061da546Spatrick     return nullptr;
6435061da546Spatrick }
6436061da546Spatrick 
EnableCompressionNextSendPacket(compression_types type)6437061da546Spatrick void RNBRemote::EnableCompressionNextSendPacket(compression_types type) {
6438061da546Spatrick   m_compression_mode = type;
6439061da546Spatrick   m_enable_compression_next_send_packet = true;
6440061da546Spatrick }
6441061da546Spatrick 
GetCompressionType()6442061da546Spatrick compression_types RNBRemote::GetCompressionType() {
6443061da546Spatrick   // The first packet we send back to the debugger after a QEnableCompression
6444061da546Spatrick   // request
6445061da546Spatrick   // should be uncompressed -- so we can indicate whether the compression was
6446061da546Spatrick   // enabled
6447061da546Spatrick   // or not via OK / Enn returns.  After that, all packets sent will be using
6448061da546Spatrick   // the
6449061da546Spatrick   // compression protocol.
6450061da546Spatrick 
6451061da546Spatrick   if (m_enable_compression_next_send_packet) {
6452061da546Spatrick     // One time, we send back "None" as our compression type
6453061da546Spatrick     m_enable_compression_next_send_packet = false;
6454061da546Spatrick     return compression_types::none;
6455061da546Spatrick   }
6456061da546Spatrick   return m_compression_mode;
6457061da546Spatrick }
6458