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 ®_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 ®_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, ®_value))
2577061da546Spatrick reg_value_ptr = ®_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, ®_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, ®_value))
2853061da546Spatrick continue;
2854061da546Spatrick
2855061da546Spatrick debugserver_regnum_with_fixed_width_hex_register_value(
2856061da546Spatrick ostrm, pid, tid, &g_reg_entries[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, ®_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(®_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, ¤tRightDict);
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, ®_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, ®ion_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 ®) {
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 ®: 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, ®_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, ®ion_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