1 | //===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Created by Greg Clayton on 12/12/07. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "RNBRemote.h" |
14 | |
15 | #include <bsm/audit.h> |
16 | #include <bsm/audit_session.h> |
17 | #include <cerrno> |
18 | #include <csignal> |
19 | #include <libproc.h> |
20 | #include <mach-o/loader.h> |
21 | #include <mach/exception_types.h> |
22 | #include <mach/mach_vm.h> |
23 | #include <mach/task_info.h> |
24 | #include <pwd.h> |
25 | #include <sys/stat.h> |
26 | #include <sys/sysctl.h> |
27 | #include <unistd.h> |
28 | |
29 | #if defined(__APPLE__) |
30 | #include <pthread.h> |
31 | #include <sched.h> |
32 | #endif |
33 | |
34 | #include "DNB.h" |
35 | #include "DNBDataRef.h" |
36 | #include "DNBLog.h" |
37 | #include "DNBThreadResumeActions.h" |
38 | #include "JSON.h" |
39 | #include "JSONGenerator.h" |
40 | #include "JSONGenerator.h" |
41 | #include "MacOSX/Genealogy.h" |
42 | #include "OsLogger.h" |
43 | #include "RNBContext.h" |
44 | #include "RNBServices.h" |
45 | #include "RNBSocket.h" |
46 | #include "StdStringExtractor.h" |
47 | |
48 | #include <compression.h> |
49 | |
50 | #include <TargetConditionals.h> |
51 | #include <algorithm> |
52 | #include <iomanip> |
53 | #include <memory> |
54 | #include <sstream> |
55 | #include <unordered_set> |
56 | |
57 | #include <CoreFoundation/CoreFoundation.h> |
58 | #include <Security/Security.h> |
59 | |
60 | // constants |
61 | |
62 | static const std::string OS_LOG_EVENTS_KEY_NAME("events" ); |
63 | static const std::string JSON_ASYNC_TYPE_KEY_NAME("type" ); |
64 | |
65 | // std::iostream formatting macros |
66 | #define RAW_HEXBASE std::setfill('0') << std::hex << std::right |
67 | #define HEXBASE '0' << 'x' << RAW_HEXBASE |
68 | #define RAWHEX8(x) RAW_HEXBASE << std::setw(2) << ((uint32_t)((uint8_t)x)) |
69 | #define RAWHEX16 RAW_HEXBASE << std::setw(4) |
70 | #define RAWHEX32 RAW_HEXBASE << std::setw(8) |
71 | #define RAWHEX64 RAW_HEXBASE << std::setw(16) |
72 | #define HEX8(x) HEXBASE << std::setw(2) << ((uint32_t)(x)) |
73 | #define HEX16 HEXBASE << std::setw(4) |
74 | #define HEX32 HEXBASE << std::setw(8) |
75 | #define HEX64 HEXBASE << std::setw(16) |
76 | #define RAW_HEX(x) RAW_HEXBASE << std::setw(sizeof(x) * 2) << (x) |
77 | #define HEX(x) HEXBASE << std::setw(sizeof(x) * 2) << (x) |
78 | #define RAWHEX_SIZE(x, sz) RAW_HEXBASE << std::setw((sz)) << (x) |
79 | #define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x) |
80 | #define STRING_WIDTH(w) std::setfill(' ') << std::setw(w) |
81 | #define LEFT_STRING_WIDTH(s, w) \ |
82 | std::left << std::setfill(' ') << std::setw(w) << (s) << std::right |
83 | #define DECIMAL std::dec << std::setfill(' ') |
84 | #define DECIMAL_WIDTH(w) DECIMAL << std::setw(w) |
85 | #define FLOAT(n, d) \ |
86 | std::setfill(' ') << std::setw((n) + (d) + 1) << std::setprecision(d) \ |
87 | << std::showpoint << std::fixed |
88 | #define INDENT_WITH_SPACES(iword_idx) \ |
89 | std::setfill(' ') << std::setw((iword_idx)) << "" |
90 | #define INDENT_WITH_TABS(iword_idx) \ |
91 | std::setfill('\t') << std::setw((iword_idx)) << "" |
92 | // Class to handle communications via gdb remote protocol. |
93 | |
94 | // Prototypes |
95 | |
96 | static std::string binary_encode_string(const std::string &s); |
97 | |
98 | // Decode a single hex character and return the hex value as a number or |
99 | // -1 if "ch" is not a hex character. |
100 | static inline int xdigit_to_sint(char ch) { |
101 | if (ch >= 'a' && ch <= 'f') |
102 | return 10 + ch - 'a'; |
103 | if (ch >= 'A' && ch <= 'F') |
104 | return 10 + ch - 'A'; |
105 | if (ch >= '0' && ch <= '9') |
106 | return ch - '0'; |
107 | return -1; |
108 | } |
109 | |
110 | // Decode a single hex ASCII byte. Return -1 on failure, a value 0-255 |
111 | // on success. |
112 | static inline int decoded_hex_ascii_char(const char *p) { |
113 | const int hi_nibble = xdigit_to_sint(ch: p[0]); |
114 | if (hi_nibble == -1) |
115 | return -1; |
116 | const int lo_nibble = xdigit_to_sint(ch: p[1]); |
117 | if (lo_nibble == -1) |
118 | return -1; |
119 | return (uint8_t)((hi_nibble << 4) + lo_nibble); |
120 | } |
121 | |
122 | // Decode a hex ASCII string back into a string |
123 | static std::string decode_hex_ascii_string(const char *p, |
124 | uint32_t max_length = UINT32_MAX) { |
125 | std::string arg; |
126 | if (p) { |
127 | for (const char *c = p; ((c - p) / 2) < max_length; c += 2) { |
128 | int ch = decoded_hex_ascii_char(p: c); |
129 | if (ch == -1) |
130 | break; |
131 | else |
132 | arg.push_back(c: ch); |
133 | } |
134 | } |
135 | return arg; |
136 | } |
137 | |
138 | uint64_t decode_uint64(const char *p, int base, char **end = nullptr, |
139 | uint64_t fail_value = 0) { |
140 | nub_addr_t addr = strtoull(nptr: p, endptr: end, base: 16); |
141 | if (addr == 0 && errno != 0) |
142 | return fail_value; |
143 | return addr; |
144 | } |
145 | |
146 | void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size, |
147 | bool swap) { |
148 | int i; |
149 | const uint8_t *p = (const uint8_t *)buf; |
150 | if (swap) { |
151 | for (i = static_cast<int>(buf_size) - 1; i >= 0; i--) |
152 | ostrm << RAWHEX8(p[i]); |
153 | } else { |
154 | for (size_t i = 0; i < buf_size; i++) |
155 | ostrm << RAWHEX8(p[i]); |
156 | } |
157 | } |
158 | |
159 | std::string cstring_to_asciihex_string(const char *str) { |
160 | std::string hex_str; |
161 | hex_str.reserve(res: strlen(s: str) * 2); |
162 | while (str && *str) { |
163 | uint8_t c = *str++; |
164 | char hexbuf[5]; |
165 | snprintf(s: hexbuf, maxlen: sizeof(hexbuf), format: "%02x" , c); |
166 | hex_str += hexbuf; |
167 | } |
168 | return hex_str; |
169 | } |
170 | |
171 | void append_hexified_string(std::ostream &ostrm, const std::string &string) { |
172 | size_t string_size = string.size(); |
173 | const char *string_buf = string.c_str(); |
174 | for (size_t i = 0; i < string_size; i++) { |
175 | ostrm << RAWHEX8(*(string_buf + i)); |
176 | } |
177 | } |
178 | |
179 | extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, |
180 | va_list args); |
181 | |
182 | // from System.framework/Versions/B/PrivateHeaders/sys/codesign.h |
183 | extern "C" { |
184 | #define CS_OPS_STATUS 0 /* return status */ |
185 | #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ |
186 | int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize); |
187 | |
188 | // from rootless.h |
189 | bool rootless_allows_task_for_pid(pid_t pid); |
190 | |
191 | // from sys/csr.h |
192 | typedef uint32_t csr_config_t; |
193 | #define CSR_ALLOW_TASK_FOR_PID (1 << 2) |
194 | int csr_check(csr_config_t mask); |
195 | } |
196 | |
197 | RNBRemote::RNBRemote() |
198 | : m_ctx(), m_comm(), m_arch(), m_continue_thread(-1), m_thread(-1), |
199 | m_mutex(), m_dispatch_queue_offsets(), |
200 | m_dispatch_queue_offsets_addr(INVALID_NUB_ADDRESS), |
201 | m_qSymbol_index(UINT32_MAX), m_packets_recvd(0), m_packets(), |
202 | m_rx_packets(), m_rx_partial_data(), m_rx_pthread(0), |
203 | m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4), |
204 | m_extended_mode(false), m_noack_mode(false), |
205 | m_thread_suffix_supported(false), m_list_threads_in_stop_reply(false), |
206 | m_compression_minsize(384), m_enable_compression_next_send_packet(false), |
207 | m_compression_mode(compression_types::none), |
208 | m_enable_error_strings(false) { |
209 | DNBLogThreadedIf(LOG_RNB_REMOTE, "%s" , __PRETTY_FUNCTION__); |
210 | CreatePacketTable(); |
211 | } |
212 | |
213 | RNBRemote::~RNBRemote() { |
214 | DNBLogThreadedIf(LOG_RNB_REMOTE, "%s" , __PRETTY_FUNCTION__); |
215 | StopReadRemoteDataThread(); |
216 | } |
217 | |
218 | void RNBRemote::CreatePacketTable() { |
219 | // Step required to add new packets: |
220 | // 1 - Add new enumeration to RNBRemote::PacketEnum |
221 | // 2 - Create the RNBRemote::HandlePacket_ function if a new function is |
222 | // needed |
223 | // 3 - Register the Packet definition with any needed callbacks in this |
224 | // function |
225 | // - If no response is needed for a command, then use NULL for the |
226 | // normal callback |
227 | // - If the packet is not supported while the target is running, use |
228 | // NULL for the async callback |
229 | // 4 - If the packet is a standard packet (starts with a '$' character |
230 | // followed by the payload and then '#' and checksum, then you are done |
231 | // else go on to step 5 |
232 | // 5 - if the packet is a fixed length packet: |
233 | // - modify the switch statement for the first character in the payload |
234 | // in RNBRemote::CommDataReceived so it doesn't reject the new packet |
235 | // type as invalid |
236 | // - modify the switch statement for the first character in the payload |
237 | // in RNBRemote::GetPacketPayload and make sure the payload of the |
238 | // packet |
239 | // is returned correctly |
240 | |
241 | std::vector<Packet> &t = m_packets; |
242 | t.push_back(x: Packet(ack, NULL, NULL, "+" , "ACK" )); |
243 | t.push_back(x: Packet(nack, NULL, NULL, "-" , "!ACK" )); |
244 | t.push_back(x: Packet(read_memory, &RNBRemote::HandlePacket_m, NULL, "m" , |
245 | "Read memory" )); |
246 | t.push_back(x: Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p" , |
247 | "Read one register" )); |
248 | t.push_back(x: Packet(read_general_regs, &RNBRemote::HandlePacket_g, NULL, "g" , |
249 | "Read registers" )); |
250 | t.push_back(x: Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M" , |
251 | "Write memory" )); |
252 | t.push_back(x: Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P" , |
253 | "Write one register" )); |
254 | t.push_back(x: Packet(write_general_regs, &RNBRemote::HandlePacket_G, NULL, "G" , |
255 | "Write registers" )); |
256 | t.push_back(x: Packet(insert_mem_bp, &RNBRemote::HandlePacket_z, NULL, "Z0" , |
257 | "Insert memory breakpoint" )); |
258 | t.push_back(x: Packet(remove_mem_bp, &RNBRemote::HandlePacket_z, NULL, "z0" , |
259 | "Remove memory breakpoint" )); |
260 | t.push_back(x: Packet(single_step, &RNBRemote::HandlePacket_s, NULL, "s" , |
261 | "Single step" )); |
262 | t.push_back(x: Packet(cont, &RNBRemote::HandlePacket_c, NULL, "c" , "continue" )); |
263 | t.push_back(x: Packet(single_step_with_sig, &RNBRemote::HandlePacket_S, NULL, |
264 | "S" , "Single step with signal" )); |
265 | t.push_back( |
266 | x: Packet(set_thread, &RNBRemote::HandlePacket_H, NULL, "H" , "Set thread" )); |
267 | t.push_back(x: Packet(halt, &RNBRemote::HandlePacket_last_signal, |
268 | &RNBRemote::HandlePacket_stop_process, "\x03" , "^C" )); |
269 | // t.push_back (Packet (use_extended_mode, |
270 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "!", "Use extended mode")); |
271 | t.push_back(x: Packet(why_halted, &RNBRemote::HandlePacket_last_signal, NULL, |
272 | "?" , "Why did target halt" )); |
273 | t.push_back( |
274 | x: Packet(set_argv, &RNBRemote::HandlePacket_A, NULL, "A" , "Set argv" )); |
275 | // t.push_back (Packet (set_bp, |
276 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "B", "Set/clear |
277 | // breakpoint")); |
278 | t.push_back(x: Packet(continue_with_sig, &RNBRemote::HandlePacket_C, NULL, "C" , |
279 | "Continue with signal" )); |
280 | t.push_back(x: Packet(detach, &RNBRemote::HandlePacket_D, NULL, "D" , |
281 | "Detach gdb from remote system" )); |
282 | // t.push_back (Packet (step_inferior_one_cycle, |
283 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "i", "Step inferior by one |
284 | // clock cycle")); |
285 | // t.push_back (Packet (signal_and_step_inf_one_cycle, |
286 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "I", "Signal inferior, then |
287 | // step one clock cycle")); |
288 | t.push_back(x: Packet(kill, &RNBRemote::HandlePacket_k, NULL, "k" , "Kill" )); |
289 | // t.push_back (Packet (restart, |
290 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "R", "Restart inferior")); |
291 | // t.push_back (Packet (search_mem_backwards, |
292 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "t", "Search memory |
293 | // backwards")); |
294 | t.push_back(x: Packet(thread_alive_p, &RNBRemote::HandlePacket_T, NULL, "T" , |
295 | "Is thread alive" )); |
296 | t.push_back(x: Packet(query_supported_features, |
297 | &RNBRemote::HandlePacket_qSupported, NULL, "qSupported" , |
298 | "Query about supported features" )); |
299 | t.push_back(x: Packet(vattach, &RNBRemote::HandlePacket_v, NULL, "vAttach" , |
300 | "Attach to a new process" )); |
301 | t.push_back(x: Packet(vattachwait, &RNBRemote::HandlePacket_v, NULL, |
302 | "vAttachWait" , |
303 | "Wait for a process to start up then attach to it" )); |
304 | t.push_back(x: Packet(vattachorwait, &RNBRemote::HandlePacket_v, NULL, |
305 | "vAttachOrWait" , "Attach to the process or if it doesn't " |
306 | "exist, wait for the process to start up " |
307 | "then attach to it" )); |
308 | t.push_back(x: Packet(vattachname, &RNBRemote::HandlePacket_v, NULL, |
309 | "vAttachName" , "Attach to an existing process by name" )); |
310 | t.push_back(x: Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, |
311 | "vCont;" , "Verbose resume with thread actions" )); |
312 | t.push_back(x: Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, |
313 | "vCont?" , |
314 | "List valid continue-with-thread-actions actions" )); |
315 | t.push_back(x: Packet(read_data_from_memory, &RNBRemote::HandlePacket_x, NULL, |
316 | "x" , "Read data from memory" )); |
317 | t.push_back(x: Packet(write_data_to_memory, &RNBRemote::HandlePacket_X, NULL, |
318 | "X" , "Write data to memory" )); |
319 | t.push_back(x: Packet(insert_hardware_bp, &RNBRemote::HandlePacket_z, NULL, "Z1" , |
320 | "Insert hardware breakpoint" )); |
321 | t.push_back(x: Packet(remove_hardware_bp, &RNBRemote::HandlePacket_z, NULL, "z1" , |
322 | "Remove hardware breakpoint" )); |
323 | t.push_back(x: Packet(insert_write_watch_bp, &RNBRemote::HandlePacket_z, NULL, |
324 | "Z2" , "Insert write watchpoint" )); |
325 | t.push_back(x: Packet(remove_write_watch_bp, &RNBRemote::HandlePacket_z, NULL, |
326 | "z2" , "Remove write watchpoint" )); |
327 | t.push_back(x: Packet(insert_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, |
328 | "Z3" , "Insert read watchpoint" )); |
329 | t.push_back(x: Packet(remove_read_watch_bp, &RNBRemote::HandlePacket_z, NULL, |
330 | "z3" , "Remove read watchpoint" )); |
331 | t.push_back(x: Packet(insert_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, |
332 | "Z4" , "Insert access watchpoint" )); |
333 | t.push_back(x: Packet(remove_access_watch_bp, &RNBRemote::HandlePacket_z, NULL, |
334 | "z4" , "Remove access watchpoint" )); |
335 | t.push_back(x: Packet(query_monitor, &RNBRemote::HandlePacket_qRcmd, NULL, |
336 | "qRcmd" , "Monitor command" )); |
337 | t.push_back(x: Packet(query_current_thread_id, &RNBRemote::HandlePacket_qC, NULL, |
338 | "qC" , "Query current thread ID" )); |
339 | t.push_back(x: Packet(query_echo, &RNBRemote::HandlePacket_qEcho, NULL, "qEcho:" , |
340 | "Echo the packet back to allow the debugger to sync up " |
341 | "with this server" )); |
342 | t.push_back(x: Packet(query_get_pid, &RNBRemote::HandlePacket_qGetPid, NULL, |
343 | "qGetPid" , "Query process id" )); |
344 | t.push_back(x: Packet(query_thread_ids_first, |
345 | &RNBRemote::HandlePacket_qThreadInfo, NULL, "qfThreadInfo" , |
346 | "Get list of active threads (first req)" )); |
347 | t.push_back(x: Packet(query_thread_ids_subsequent, |
348 | &RNBRemote::HandlePacket_qThreadInfo, NULL, "qsThreadInfo" , |
349 | "Get list of active threads (subsequent req)" )); |
350 | // APPLE LOCAL: qThreadStopInfo |
351 | // syntax: qThreadStopInfoTTTT |
352 | // TTTT is hex thread ID |
353 | t.push_back(x: Packet(query_thread_stop_info, |
354 | &RNBRemote::HandlePacket_qThreadStopInfo, NULL, |
355 | "qThreadStopInfo" , |
356 | "Get detailed info on why the specified thread stopped" )); |
357 | t.push_back(x: Packet(query_thread_extra_info, |
358 | &RNBRemote::HandlePacket_qThreadExtraInfo, NULL, |
359 | "qThreadExtraInfo" , "Get printable status of a thread" )); |
360 | // t.push_back (Packet (query_image_offsets, |
361 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qOffsets", "Report offset |
362 | // of loaded program")); |
363 | t.push_back(x: Packet( |
364 | query_launch_success, &RNBRemote::HandlePacket_qLaunchSuccess, NULL, |
365 | "qLaunchSuccess" , "Report the success or failure of the launch attempt" )); |
366 | t.push_back( |
367 | x: Packet(query_register_info, &RNBRemote::HandlePacket_qRegisterInfo, NULL, |
368 | "qRegisterInfo" , |
369 | "Dynamically discover remote register context information." )); |
370 | t.push_back(x: Packet( |
371 | query_shlib_notify_info_addr, &RNBRemote::HandlePacket_qShlibInfoAddr, |
372 | NULL, "qShlibInfoAddr" , "Returns the address that contains info needed " |
373 | "for getting shared library notifications" )); |
374 | t.push_back(x: Packet(query_step_packet_supported, |
375 | &RNBRemote::HandlePacket_qStepPacketSupported, NULL, |
376 | "qStepPacketSupported" , |
377 | "Replys with OK if the 's' packet is supported." )); |
378 | t.push_back( |
379 | x: Packet(query_vattachorwait_supported, |
380 | &RNBRemote::HandlePacket_qVAttachOrWaitSupported, NULL, |
381 | "qVAttachOrWaitSupported" , |
382 | "Replys with OK if the 'vAttachOrWait' packet is supported." )); |
383 | t.push_back( |
384 | x: Packet(query_sync_thread_state_supported, |
385 | &RNBRemote::HandlePacket_qSyncThreadStateSupported, NULL, |
386 | "qSyncThreadStateSupported" , |
387 | "Replys with OK if the 'QSyncThreadState:' packet is supported." )); |
388 | t.push_back(x: Packet( |
389 | query_host_info, &RNBRemote::HandlePacket_qHostInfo, NULL, "qHostInfo" , |
390 | "Replies with multiple 'key:value;' tuples appended to each other." )); |
391 | t.push_back(x: Packet( |
392 | query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion, |
393 | NULL, "qGDBServerVersion" , |
394 | "Replies with multiple 'key:value;' tuples appended to each other." )); |
395 | t.push_back(x: Packet( |
396 | query_process_info, &RNBRemote::HandlePacket_qProcessInfo, NULL, |
397 | "qProcessInfo" , |
398 | "Replies with multiple 'key:value;' tuples appended to each other." )); |
399 | t.push_back(x: Packet( |
400 | query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol, NULL, "qSymbol:" , |
401 | "Notify that host debugger is ready to do symbol lookups" )); |
402 | t.push_back(x: Packet(enable_error_strings, |
403 | &RNBRemote::HandlePacket_QEnableErrorStrings, NULL, |
404 | "QEnableErrorStrings" , |
405 | "Tell " DEBUGSERVER_PROGRAM_NAME |
406 | " it can append descriptive error messages in replies." )); |
407 | t.push_back(x: Packet(json_query_thread_extended_info, |
408 | &RNBRemote::HandlePacket_jThreadExtendedInfo, NULL, |
409 | "jThreadExtendedInfo" , |
410 | "Replies with JSON data of thread extended information." )); |
411 | t.push_back(x: Packet(json_query_get_loaded_dynamic_libraries_infos, |
412 | &RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos, |
413 | NULL, "jGetLoadedDynamicLibrariesInfos" , |
414 | "Replies with JSON data of all the shared libraries " |
415 | "loaded in this process." )); |
416 | t.push_back( |
417 | x: Packet(json_query_threads_info, &RNBRemote::HandlePacket_jThreadsInfo, |
418 | NULL, "jThreadsInfo" , |
419 | "Replies with JSON data with information about all threads." )); |
420 | t.push_back(x: Packet(json_query_get_shared_cache_info, |
421 | &RNBRemote::HandlePacket_jGetSharedCacheInfo, NULL, |
422 | "jGetSharedCacheInfo" , "Replies with JSON data about the " |
423 | "location and uuid of the shared " |
424 | "cache in the inferior process." )); |
425 | t.push_back(x: Packet(start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode, |
426 | NULL, "QStartNoAckMode" , |
427 | "Request that " DEBUGSERVER_PROGRAM_NAME |
428 | " stop acking remote protocol packets" )); |
429 | t.push_back(x: Packet(prefix_reg_packets_with_tid, |
430 | &RNBRemote::HandlePacket_QThreadSuffixSupported, NULL, |
431 | "QThreadSuffixSupported" , |
432 | "Check if thread specific packets (register packets 'g', " |
433 | "'G', 'p', and 'P') support having the thread ID appended " |
434 | "to the end of the command" )); |
435 | t.push_back(x: Packet(set_logging_mode, &RNBRemote::HandlePacket_QSetLogging, |
436 | NULL, "QSetLogging:" , "Turn on log channels in debugserver" )); |
437 | t.push_back(x: Packet(set_ignored_exceptions, &RNBRemote::HandlePacket_QSetIgnoredExceptions, |
438 | NULL, "QSetIgnoredExceptions:" , "Set the exception types " |
439 | "debugserver won't wait for, allowing " |
440 | "them to be turned into the equivalent " |
441 | "BSD signals by the normal means." )); |
442 | t.push_back(x: Packet( |
443 | set_max_packet_size, &RNBRemote::HandlePacket_QSetMaxPacketSize, NULL, |
444 | "QSetMaxPacketSize:" , |
445 | "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle" )); |
446 | t.push_back(x: Packet( |
447 | set_max_payload_size, &RNBRemote::HandlePacket_QSetMaxPayloadSize, NULL, |
448 | "QSetMaxPayloadSize:" , "Tell " DEBUGSERVER_PROGRAM_NAME |
449 | " the max sized payload gdb can handle" )); |
450 | t.push_back( |
451 | x: Packet(set_environment_variable, &RNBRemote::HandlePacket_QEnvironment, |
452 | NULL, "QEnvironment:" , |
453 | "Add an environment variable to the inferior's environment" )); |
454 | t.push_back( |
455 | x: Packet(set_environment_variable_hex, |
456 | &RNBRemote::HandlePacket_QEnvironmentHexEncoded, NULL, |
457 | "QEnvironmentHexEncoded:" , |
458 | "Add an environment variable to the inferior's environment" )); |
459 | t.push_back(x: Packet(set_launch_arch, &RNBRemote::HandlePacket_QLaunchArch, |
460 | NULL, "QLaunchArch:" , "Set the architecture to use when " |
461 | "launching a process for hosts that " |
462 | "can run multiple architecture " |
463 | "slices from universal files." )); |
464 | t.push_back(x: Packet(set_disable_aslr, &RNBRemote::HandlePacket_QSetDisableASLR, |
465 | NULL, "QSetDisableASLR:" , |
466 | "Set whether to disable ASLR when launching the process " |
467 | "with the set argv ('A') packet" )); |
468 | t.push_back(x: Packet(set_stdin, &RNBRemote::HandlePacket_QSetSTDIO, NULL, |
469 | "QSetSTDIN:" , "Set the standard input for a process to be " |
470 | "launched with the 'A' packet" )); |
471 | t.push_back(x: Packet(set_stdout, &RNBRemote::HandlePacket_QSetSTDIO, NULL, |
472 | "QSetSTDOUT:" , "Set the standard output for a process to " |
473 | "be launched with the 'A' packet" )); |
474 | t.push_back(x: Packet(set_stderr, &RNBRemote::HandlePacket_QSetSTDIO, NULL, |
475 | "QSetSTDERR:" , "Set the standard error for a process to " |
476 | "be launched with the 'A' packet" )); |
477 | t.push_back(x: Packet(set_working_dir, &RNBRemote::HandlePacket_QSetWorkingDir, |
478 | NULL, "QSetWorkingDir:" , "Set the working directory for a " |
479 | "process to be launched with the " |
480 | "'A' packet" )); |
481 | t.push_back(x: Packet(set_list_threads_in_stop_reply, |
482 | &RNBRemote::HandlePacket_QListThreadsInStopReply, NULL, |
483 | "QListThreadsInStopReply" , |
484 | "Set if the 'threads' key should be added to the stop " |
485 | "reply packets with a list of all thread IDs." )); |
486 | t.push_back(x: Packet( |
487 | sync_thread_state, &RNBRemote::HandlePacket_QSyncThreadState, NULL, |
488 | "QSyncThreadState:" , "Do whatever is necessary to make sure 'thread' is " |
489 | "in a safe state to call functions on." )); |
490 | // t.push_back (Packet (pass_signals_to_inferior, |
491 | // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "QPassSignals:", "Specify |
492 | // which signals are passed to the inferior")); |
493 | t.push_back(x: Packet(allocate_memory, &RNBRemote::HandlePacket_AllocateMemory, |
494 | NULL, "_M" , "Allocate memory in the inferior process." )); |
495 | t.push_back(x: Packet(deallocate_memory, |
496 | &RNBRemote::HandlePacket_DeallocateMemory, NULL, "_m" , |
497 | "Deallocate memory in the inferior process." )); |
498 | t.push_back(x: Packet( |
499 | save_register_state, &RNBRemote::HandlePacket_SaveRegisterState, NULL, |
500 | "QSaveRegisterState" , "Save the register state for the current thread " |
501 | "and return a decimal save ID." )); |
502 | t.push_back(x: Packet(restore_register_state, |
503 | &RNBRemote::HandlePacket_RestoreRegisterState, NULL, |
504 | "QRestoreRegisterState:" , |
505 | "Restore the register state given a save ID previously " |
506 | "returned from a call to QSaveRegisterState." )); |
507 | t.push_back(x: Packet( |
508 | memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL, |
509 | "qMemoryRegionInfo" , "Return size and attributes of a memory region that " |
510 | "contains the given address" )); |
511 | t.push_back(x: Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData, |
512 | NULL, "qGetProfileData" , |
513 | "Return profiling data of the current target." )); |
514 | t.push_back(x: Packet(set_enable_profiling, |
515 | &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL, |
516 | "QSetEnableAsyncProfiling" , |
517 | "Enable or disable the profiling of current target." )); |
518 | t.push_back(x: Packet(enable_compression, |
519 | &RNBRemote::HandlePacket_QEnableCompression, NULL, |
520 | "QEnableCompression:" , |
521 | "Enable compression for the remainder of the connection" )); |
522 | t.push_back(x: Packet(watchpoint_support_info, |
523 | &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL, |
524 | "qWatchpointSupportInfo" , |
525 | "Return the number of supported hardware watchpoints" )); |
526 | t.push_back(x: Packet(set_process_event, |
527 | &RNBRemote::HandlePacket_QSetProcessEvent, NULL, |
528 | "QSetProcessEvent:" , "Set a process event, to be passed " |
529 | "to the process, can be set before " |
530 | "the process is started, or after." )); |
531 | t.push_back( |
532 | x: Packet(set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError, |
533 | NULL, "QSetDetachOnError:" , |
534 | "Set whether debugserver will detach (1) or kill (0) from the " |
535 | "process it is controlling if it loses connection to lldb." )); |
536 | t.push_back(x: Packet( |
537 | speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:" , |
538 | "Test the maximum speed at which packet can be sent/received." )); |
539 | t.push_back(x: Packet(query_transfer, &RNBRemote::HandlePacket_qXfer, NULL, |
540 | "qXfer:" , "Support the qXfer packet." )); |
541 | t.push_back(x: Packet(json_query_dyld_process_state, |
542 | &RNBRemote::HandlePacket_jGetDyldProcessState, NULL, |
543 | "jGetDyldProcessState" , |
544 | "Query the process state from dyld." )); |
545 | } |
546 | |
547 | void RNBRemote::FlushSTDIO() { |
548 | if (m_ctx.HasValidProcessID()) { |
549 | nub_process_t pid = m_ctx.ProcessID(); |
550 | char buf[256]; |
551 | nub_size_t count; |
552 | do { |
553 | count = DNBProcessGetAvailableSTDOUT(pid, buf, buf_size: sizeof(buf)); |
554 | if (count > 0) { |
555 | SendSTDOUTPacket(buf, buf_size: count); |
556 | } |
557 | } while (count > 0); |
558 | |
559 | do { |
560 | count = DNBProcessGetAvailableSTDERR(pid, buf, buf_size: sizeof(buf)); |
561 | if (count > 0) { |
562 | SendSTDERRPacket(buf, buf_size: count); |
563 | } |
564 | } while (count > 0); |
565 | } |
566 | } |
567 | |
568 | void RNBRemote::SendAsyncProfileData() { |
569 | if (m_ctx.HasValidProcessID()) { |
570 | nub_process_t pid = m_ctx.ProcessID(); |
571 | char buf[1024]; |
572 | nub_size_t count; |
573 | do { |
574 | count = DNBProcessGetAvailableProfileData(pid, buf, buf_size: sizeof(buf)); |
575 | if (count > 0) { |
576 | SendAsyncProfileDataPacket(buf, buf_size: count); |
577 | } |
578 | } while (count > 0); |
579 | } |
580 | } |
581 | |
582 | rnb_err_t RNBRemote::SendHexEncodedBytePacket(const char *, |
583 | const void *buf, size_t buf_len, |
584 | const char *) { |
585 | std::ostringstream packet_sstrm; |
586 | // Append the header cstr if there was one |
587 | if (header && header[0]) |
588 | packet_sstrm << header; |
589 | nub_size_t i; |
590 | const uint8_t *ubuf8 = (const uint8_t *)buf; |
591 | for (i = 0; i < buf_len; i++) { |
592 | packet_sstrm << RAWHEX8(ubuf8[i]); |
593 | } |
594 | // Append the footer cstr if there was one |
595 | if (footer && footer[0]) |
596 | packet_sstrm << footer; |
597 | |
598 | return SendPacket(packet_sstrm.str()); |
599 | } |
600 | |
601 | rnb_err_t RNBRemote::SendSTDOUTPacket(char *buf, nub_size_t buf_size) { |
602 | if (buf_size == 0) |
603 | return rnb_success; |
604 | return SendHexEncodedBytePacket(header: "O" , buf, buf_len: buf_size, NULL); |
605 | } |
606 | |
607 | rnb_err_t RNBRemote::SendSTDERRPacket(char *buf, nub_size_t buf_size) { |
608 | if (buf_size == 0) |
609 | return rnb_success; |
610 | return SendHexEncodedBytePacket(header: "O" , buf, buf_len: buf_size, NULL); |
611 | } |
612 | |
613 | // This makes use of asynchronous bit 'A' in the gdb remote protocol. |
614 | rnb_err_t RNBRemote::SendAsyncProfileDataPacket(char *buf, |
615 | nub_size_t buf_size) { |
616 | if (buf_size == 0) |
617 | return rnb_success; |
618 | |
619 | std::string packet("A" ); |
620 | packet.append(s: buf, n: buf_size); |
621 | return SendPacket(packet); |
622 | } |
623 | |
624 | rnb_err_t |
625 | RNBRemote::SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary) { |
626 | std::ostringstream stream; |
627 | // We're choosing something that is easy to spot if we somehow get one |
628 | // of these coming out at the wrong time (i.e. when the remote side |
629 | // is not waiting for a process control completion response). |
630 | stream << "JSON-async:" ; |
631 | dictionary.DumpBinaryEscaped(s&: stream); |
632 | return SendPacket(stream.str()); |
633 | } |
634 | |
635 | // Given a std::string packet contents to send, possibly encode/compress it. |
636 | // If compression is enabled, the returned std::string will be in one of two |
637 | // forms: |
638 | // |
639 | // N<original packet contents uncompressed> |
640 | // C<size of original decompressed packet>:<packet compressed with the |
641 | // requested compression scheme> |
642 | // |
643 | // If compression is not requested, the original packet contents are returned |
644 | |
645 | std::string RNBRemote::CompressString(const std::string &orig) { |
646 | std::string compressed; |
647 | compression_types compression_type = GetCompressionType(); |
648 | if (compression_type != compression_types::none) { |
649 | bool compress_this_packet = false; |
650 | |
651 | if (orig.size() > m_compression_minsize) { |
652 | compress_this_packet = true; |
653 | } |
654 | |
655 | if (compress_this_packet) { |
656 | const size_t encoded_data_buf_size = orig.size() + 128; |
657 | std::vector<uint8_t> encoded_data(encoded_data_buf_size); |
658 | size_t compressed_size = 0; |
659 | |
660 | // Allocate a scratch buffer for libcompression the first |
661 | // time we see a different compression type; reuse it in |
662 | // all compression_encode_buffer calls so it doesn't need |
663 | // to allocate / free its own scratch buffer each time. |
664 | // This buffer will only be freed when compression type |
665 | // changes; otherwise it will persist until debugserver |
666 | // exit. |
667 | |
668 | static compression_types g_libcompress_scratchbuf_type = compression_types::none; |
669 | static void *g_libcompress_scratchbuf = nullptr; |
670 | |
671 | if (g_libcompress_scratchbuf_type != compression_type) { |
672 | if (g_libcompress_scratchbuf) { |
673 | free (ptr: g_libcompress_scratchbuf); |
674 | g_libcompress_scratchbuf = nullptr; |
675 | } |
676 | size_t scratchbuf_size = 0; |
677 | switch (compression_type) { |
678 | case compression_types::lz4: |
679 | scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZ4_RAW); |
680 | break; |
681 | case compression_types::zlib_deflate: |
682 | scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_ZLIB); |
683 | break; |
684 | case compression_types::lzma: |
685 | scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZMA); |
686 | break; |
687 | case compression_types::lzfse: |
688 | scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZFSE); |
689 | break; |
690 | default: |
691 | break; |
692 | } |
693 | if (scratchbuf_size > 0) { |
694 | g_libcompress_scratchbuf = (void*) malloc (size: scratchbuf_size); |
695 | g_libcompress_scratchbuf_type = compression_type; |
696 | } |
697 | } |
698 | |
699 | if (compression_type == compression_types::lz4) { |
700 | compressed_size = compression_encode_buffer( |
701 | encoded_data.data(), encoded_data_buf_size, |
702 | (const uint8_t *)orig.c_str(), orig.size(), |
703 | g_libcompress_scratchbuf, |
704 | COMPRESSION_LZ4_RAW); |
705 | } |
706 | if (compression_type == compression_types::zlib_deflate) { |
707 | compressed_size = compression_encode_buffer( |
708 | encoded_data.data(), encoded_data_buf_size, |
709 | (const uint8_t *)orig.c_str(), orig.size(), |
710 | g_libcompress_scratchbuf, |
711 | COMPRESSION_ZLIB); |
712 | } |
713 | if (compression_type == compression_types::lzma) { |
714 | compressed_size = compression_encode_buffer( |
715 | encoded_data.data(), encoded_data_buf_size, |
716 | (const uint8_t *)orig.c_str(), orig.size(), |
717 | g_libcompress_scratchbuf, |
718 | COMPRESSION_LZMA); |
719 | } |
720 | if (compression_type == compression_types::lzfse) { |
721 | compressed_size = compression_encode_buffer( |
722 | encoded_data.data(), encoded_data_buf_size, |
723 | (const uint8_t *)orig.c_str(), orig.size(), |
724 | g_libcompress_scratchbuf, |
725 | COMPRESSION_LZFSE); |
726 | } |
727 | |
728 | if (compressed_size > 0) { |
729 | compressed.clear(); |
730 | compressed.reserve(res: compressed_size); |
731 | compressed = "C" ; |
732 | char numbuf[16]; |
733 | snprintf(s: numbuf, maxlen: sizeof(numbuf), format: "%zu:" , orig.size()); |
734 | numbuf[sizeof(numbuf) - 1] = '\0'; |
735 | compressed.append(s: numbuf); |
736 | |
737 | for (size_t i = 0; i < compressed_size; i++) { |
738 | uint8_t byte = encoded_data[i]; |
739 | if (byte == '#' || byte == '$' || byte == '}' || byte == '*' || |
740 | byte == '\0') { |
741 | compressed.push_back(c: 0x7d); |
742 | compressed.push_back(c: byte ^ 0x20); |
743 | } else { |
744 | compressed.push_back(c: byte); |
745 | } |
746 | } |
747 | } else { |
748 | compressed = "N" + orig; |
749 | } |
750 | } else { |
751 | compressed = "N" + orig; |
752 | } |
753 | } else { |
754 | compressed = orig; |
755 | } |
756 | |
757 | return compressed; |
758 | } |
759 | |
760 | rnb_err_t RNBRemote::SendPacket(const std::string &s) { |
761 | DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called" , |
762 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
763 | __FUNCTION__, s.c_str()); |
764 | |
765 | std::string s_compressed = CompressString(orig: s); |
766 | |
767 | std::string sendpacket = "$" + s_compressed + "#" ; |
768 | int cksum = 0; |
769 | char hexbuf[5]; |
770 | |
771 | if (m_noack_mode) { |
772 | sendpacket += "00" ; |
773 | } else { |
774 | for (size_t i = 0; i != s_compressed.size(); ++i) |
775 | cksum += s_compressed[i]; |
776 | snprintf(s: hexbuf, maxlen: sizeof hexbuf, format: "%02x" , cksum & 0xff); |
777 | sendpacket += hexbuf; |
778 | } |
779 | |
780 | rnb_err_t err = m_comm.Write(buffer: sendpacket.c_str(), length: sendpacket.size()); |
781 | if (err != rnb_success) |
782 | return err; |
783 | |
784 | if (m_noack_mode) |
785 | return rnb_success; |
786 | |
787 | std::string reply; |
788 | RNBRemote::Packet packet; |
789 | err = GetPacket(packet_data&: reply, packet_info&: packet, wait: true); |
790 | |
791 | if (err != rnb_success) { |
792 | DNBLogThreadedIf(LOG_RNB_REMOTE, |
793 | "%8d RNBRemote::%s (%s) got error trying to get reply..." , |
794 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
795 | __FUNCTION__, sendpacket.c_str()); |
796 | return err; |
797 | } |
798 | |
799 | DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) got reply: '%s'" , |
800 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
801 | __FUNCTION__, sendpacket.c_str(), reply.c_str()); |
802 | |
803 | if (packet.type == ack) |
804 | return rnb_success; |
805 | |
806 | // Should we try to resend the packet at this layer? |
807 | // if (packet.command == nack) |
808 | return rnb_err; |
809 | } |
810 | |
811 | rnb_err_t RNBRemote::SendErrorPacket(std::string errcode, |
812 | const std::string &errmsg) { |
813 | if (m_enable_error_strings && !errmsg.empty()) { |
814 | errcode += ";" ; |
815 | errcode += cstring_to_asciihex_string(str: errmsg.c_str()); |
816 | } |
817 | return SendPacket(s: errcode); |
818 | } |
819 | |
820 | /* Get a packet via gdb remote protocol. |
821 | Strip off the prefix/suffix, verify the checksum to make sure |
822 | a valid packet was received, send an ACK if they match. */ |
823 | |
824 | rnb_err_t RNBRemote::GetPacketPayload(std::string &return_packet) { |
825 | // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s called", |
826 | // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); |
827 | |
828 | { |
829 | PThreadMutex::Locker locker(m_mutex); |
830 | if (m_rx_packets.empty()) { |
831 | // Only reset the remote command available event if we have no more |
832 | // packets |
833 | m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available); |
834 | // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s error: no packets |
835 | // available...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
836 | // __FUNCTION__); |
837 | return rnb_err; |
838 | } |
839 | |
840 | // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s has %u queued packets", |
841 | // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, |
842 | // m_rx_packets.size()); |
843 | return_packet.swap(s&: m_rx_packets.front()); |
844 | m_rx_packets.pop_front(); |
845 | |
846 | if (m_rx_packets.empty()) { |
847 | // Reset the remote command available event if we have no more packets |
848 | m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available); |
849 | } |
850 | } |
851 | |
852 | // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s: '%s'", |
853 | // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, |
854 | // return_packet.c_str()); |
855 | |
856 | switch (return_packet[0]) { |
857 | case '+': |
858 | case '-': |
859 | case '\x03': |
860 | break; |
861 | |
862 | case '$': { |
863 | long packet_checksum = 0; |
864 | if (!m_noack_mode) { |
865 | for (size_t i = return_packet.size() - 2; i < return_packet.size(); ++i) { |
866 | char checksum_char = tolower(c: return_packet[i]); |
867 | if (!isxdigit(checksum_char)) { |
868 | m_comm.Write(buffer: "-" , length: 1); |
869 | DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s error: packet " |
870 | "with invalid checksum characters: " |
871 | "%s" , |
872 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
873 | __FUNCTION__, return_packet.c_str()); |
874 | return rnb_err; |
875 | } |
876 | } |
877 | packet_checksum = |
878 | strtol(nptr: &return_packet[return_packet.size() - 2], NULL, base: 16); |
879 | } |
880 | |
881 | return_packet.erase(pos: 0, n: 1); // Strip the leading '$' |
882 | return_packet.erase(pos: return_packet.size() - 3); // Strip the #XX checksum |
883 | |
884 | if (!m_noack_mode) { |
885 | // Compute the checksum |
886 | int computed_checksum = 0; |
887 | for (std::string::iterator it = return_packet.begin(); |
888 | it != return_packet.end(); ++it) { |
889 | computed_checksum += *it; |
890 | } |
891 | |
892 | if (packet_checksum == (computed_checksum & 0xff)) { |
893 | // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for |
894 | // '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
895 | // __FUNCTION__, return_packet.c_str()); |
896 | m_comm.Write(buffer: "+" , length: 1); |
897 | } else { |
898 | DNBLogThreadedIf( |
899 | LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s' (error: " |
900 | "packet checksum mismatch (0x%2.2lx != 0x%2.2x))" , |
901 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, |
902 | return_packet.c_str(), packet_checksum, computed_checksum); |
903 | m_comm.Write(buffer: "-" , length: 1); |
904 | return rnb_err; |
905 | } |
906 | } |
907 | } break; |
908 | |
909 | default: |
910 | DNBLogThreadedIf(LOG_RNB_REMOTE, |
911 | "%8u RNBRemote::%s tossing unexpected packet???? %s" , |
912 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
913 | __FUNCTION__, return_packet.c_str()); |
914 | if (!m_noack_mode) |
915 | m_comm.Write(buffer: "-" , length: 1); |
916 | return rnb_err; |
917 | } |
918 | |
919 | return rnb_success; |
920 | } |
921 | |
922 | rnb_err_t RNBRemote::HandlePacket_UNIMPLEMENTED(const char *p) { |
923 | DNBLogThreadedIf(LOG_RNB_MAX, "%8u RNBRemote::%s(\"%s\")" , |
924 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
925 | __FUNCTION__, p ? p : "NULL" ); |
926 | return SendPacket(s: "" ); |
927 | } |
928 | |
929 | rnb_err_t RNBRemote::HandlePacket_ILLFORMED(const char *file, int line, |
930 | const char *p, |
931 | const char *description) { |
932 | DNBLogThreadedIf(LOG_RNB_PACKETS, "%8u %s:%i ILLFORMED: '%s' (%s)" , |
933 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), file, |
934 | line, __FUNCTION__, p); |
935 | return SendErrorPacket(errcode: "E03" ); |
936 | } |
937 | |
938 | rnb_err_t RNBRemote::GetPacket(std::string &packet_payload, |
939 | RNBRemote::Packet &packet_info, bool wait) { |
940 | std::string payload; |
941 | rnb_err_t err = GetPacketPayload(return_packet&: payload); |
942 | if (err != rnb_success) { |
943 | PThreadEvent &events = m_ctx.Events(); |
944 | nub_event_t set_events = events.GetEventBits(); |
945 | // TODO: add timeout version of GetPacket?? We would then need to pass |
946 | // that timeout value along to DNBProcessTimedWaitForEvent. |
947 | if (!wait || ((set_events & RNBContext::event_read_thread_running) == 0)) |
948 | return err; |
949 | |
950 | const nub_event_t events_to_wait_for = |
951 | RNBContext::event_read_packet_available | |
952 | RNBContext::event_read_thread_exiting; |
953 | |
954 | while ((set_events = events.WaitForSetEvents(mask: events_to_wait_for)) != 0) { |
955 | if (set_events & RNBContext::event_read_packet_available) { |
956 | // Try the queue again now that we got an event |
957 | err = GetPacketPayload(return_packet&: payload); |
958 | if (err == rnb_success) |
959 | break; |
960 | } |
961 | |
962 | if (set_events & RNBContext::event_read_thread_exiting) |
963 | err = rnb_not_connected; |
964 | |
965 | if (err == rnb_not_connected) |
966 | return err; |
967 | } |
968 | while (err == rnb_err) |
969 | ; |
970 | |
971 | if (set_events == 0) |
972 | err = rnb_not_connected; |
973 | } |
974 | |
975 | if (err == rnb_success) { |
976 | Packet::iterator it; |
977 | for (it = m_packets.begin(); it != m_packets.end(); ++it) { |
978 | if (payload.compare(pos: 0, n: it->abbrev.size(), str: it->abbrev) == 0) |
979 | break; |
980 | } |
981 | |
982 | // A packet we don't have an entry for. This can happen when we |
983 | // get a packet that we don't know about or support. We just reply |
984 | // accordingly and go on. |
985 | if (it == m_packets.end()) { |
986 | DNBLogThreadedIf(LOG_RNB_PACKETS, "unimplemented packet: '%s'" , |
987 | payload.c_str()); |
988 | HandlePacket_UNIMPLEMENTED(p: payload.c_str()); |
989 | return rnb_err; |
990 | } else { |
991 | packet_info = *it; |
992 | packet_payload = payload; |
993 | } |
994 | } |
995 | return err; |
996 | } |
997 | |
998 | rnb_err_t RNBRemote::HandleAsyncPacket(PacketEnum *type) { |
999 | DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s" , |
1000 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
1001 | __FUNCTION__); |
1002 | static DNBTimer g_packetTimer(true); |
1003 | rnb_err_t err = rnb_err; |
1004 | std::string packet_data; |
1005 | RNBRemote::Packet packet_info; |
1006 | err = GetPacket(packet_payload&: packet_data, packet_info, wait: false); |
1007 | |
1008 | if (err == rnb_success) { |
1009 | if (!packet_data.empty() && isprint(packet_data[0])) |
1010 | DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS, |
1011 | "HandleAsyncPacket (\"%s\");" , packet_data.c_str()); |
1012 | else |
1013 | DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS, |
1014 | "HandleAsyncPacket (%s);" , |
1015 | packet_info.printable_name.c_str()); |
1016 | |
1017 | HandlePacketCallback packet_callback = packet_info.async; |
1018 | if (packet_callback != NULL) { |
1019 | if (type != NULL) |
1020 | *type = packet_info.type; |
1021 | return (this->*packet_callback)(packet_data.c_str()); |
1022 | } |
1023 | } |
1024 | |
1025 | return err; |
1026 | } |
1027 | |
1028 | rnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) { |
1029 | static DNBTimer g_packetTimer(true); |
1030 | |
1031 | // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", |
1032 | // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); |
1033 | rnb_err_t err = rnb_err; |
1034 | std::string packet_data; |
1035 | RNBRemote::Packet packet_info; |
1036 | err = GetPacket(packet_payload&: packet_data, packet_info, wait: false); |
1037 | |
1038 | if (err == rnb_success) { |
1039 | DNBLogThreadedIf(LOG_RNB_REMOTE, "HandleReceivedPacket (\"%s\");" , |
1040 | packet_data.c_str()); |
1041 | HandlePacketCallback packet_callback = packet_info.normal; |
1042 | if (packet_callback != NULL) { |
1043 | if (type != NULL) |
1044 | *type = packet_info.type; |
1045 | return (this->*packet_callback)(packet_data.c_str()); |
1046 | } else { |
1047 | // Do not fall through to end of this function, if we have valid |
1048 | // packet_info and it has a NULL callback, then we need to respect |
1049 | // that it may not want any response or anything to be done. |
1050 | return err; |
1051 | } |
1052 | } |
1053 | return rnb_err; |
1054 | } |
1055 | |
1056 | void RNBRemote::CommDataReceived(const std::string &new_data) { |
1057 | // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", |
1058 | // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); |
1059 | { |
1060 | // Put the packet data into the buffer in a thread safe fashion |
1061 | PThreadMutex::Locker locker(m_mutex); |
1062 | |
1063 | std::string data; |
1064 | // See if we have any left over data from a previous call to this |
1065 | // function? |
1066 | if (!m_rx_partial_data.empty()) { |
1067 | // We do, so lets start with that data |
1068 | data.swap(s&: m_rx_partial_data); |
1069 | } |
1070 | // Append the new incoming data |
1071 | data += new_data; |
1072 | |
1073 | // Parse up the packets into gdb remote packets |
1074 | size_t idx = 0; |
1075 | const size_t data_size = data.size(); |
1076 | |
1077 | while (idx < data_size) { |
1078 | // end_idx must be one past the last valid packet byte. Start |
1079 | // it off with an invalid value that is the same as the current |
1080 | // index. |
1081 | size_t end_idx = idx; |
1082 | |
1083 | switch (data[idx]) { |
1084 | case '+': // Look for ack |
1085 | case '-': // Look for cancel |
1086 | case '\x03': // ^C to halt target |
1087 | end_idx = idx + 1; // The command is one byte long... |
1088 | break; |
1089 | |
1090 | case '$': |
1091 | // Look for a standard gdb packet? |
1092 | end_idx = data.find(c: '#', pos: idx + 1); |
1093 | if (end_idx == std::string::npos || end_idx + 3 > data_size) { |
1094 | end_idx = std::string::npos; |
1095 | } else { |
1096 | // Add two for the checksum bytes and 1 to point to the |
1097 | // byte just past the end of this packet |
1098 | end_idx += 3; |
1099 | } |
1100 | break; |
1101 | |
1102 | default: |
1103 | break; |
1104 | } |
1105 | |
1106 | if (end_idx == std::string::npos) { |
1107 | // Not all data may be here for the packet yet, save it for |
1108 | // next time through this function. |
1109 | m_rx_partial_data += data.substr(pos: idx); |
1110 | // DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s saving data for |
1111 | // later[%u, npos): |
1112 | // '%s'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
1113 | // __FUNCTION__, idx, m_rx_partial_data.c_str()); |
1114 | idx = end_idx; |
1115 | } else if (idx < end_idx) { |
1116 | m_packets_recvd++; |
1117 | // Hack to get rid of initial '+' ACK??? |
1118 | if (m_packets_recvd == 1 && (end_idx == idx + 1) && data[idx] == '+') { |
1119 | // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s throwing first |
1120 | // ACK away....[%u, npos): |
1121 | // '+'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
1122 | // __FUNCTION__, idx); |
1123 | } else { |
1124 | // We have a valid packet... |
1125 | m_rx_packets.push_back(x: data.substr(pos: idx, n: end_idx - idx)); |
1126 | DNBLogThreadedIf(LOG_RNB_PACKETS, "getpkt: %s" , |
1127 | m_rx_packets.back().c_str()); |
1128 | } |
1129 | idx = end_idx; |
1130 | } else { |
1131 | DNBLogThreadedIf(LOG_RNB_MAX, |
1132 | "%8d RNBRemote::%s tossing junk byte at %c" , |
1133 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
1134 | __FUNCTION__, data[idx]); |
1135 | idx = idx + 1; |
1136 | } |
1137 | } |
1138 | } |
1139 | |
1140 | if (!m_rx_packets.empty()) { |
1141 | // Let the main thread know we have received a packet |
1142 | |
1143 | // DNBLogThreadedIf (LOG_RNB_EVENTS, "%8d RNBRemote::%s called |
1144 | // events.SetEvent(RNBContext::event_read_packet_available)", |
1145 | // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); |
1146 | PThreadEvent &events = m_ctx.Events(); |
1147 | events.SetEvents(RNBContext::event_read_packet_available); |
1148 | } |
1149 | } |
1150 | |
1151 | rnb_err_t RNBRemote::GetCommData() { |
1152 | // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", |
1153 | // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); |
1154 | std::string comm_data; |
1155 | rnb_err_t err = m_comm.Read(p&: comm_data); |
1156 | if (err == rnb_success) { |
1157 | if (!comm_data.empty()) |
1158 | CommDataReceived(new_data: comm_data); |
1159 | } |
1160 | return err; |
1161 | } |
1162 | |
1163 | void RNBRemote::StartReadRemoteDataThread() { |
1164 | DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called" , |
1165 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
1166 | __FUNCTION__); |
1167 | PThreadEvent &events = m_ctx.Events(); |
1168 | if ((events.GetEventBits() & RNBContext::event_read_thread_running) == 0) { |
1169 | events.ResetEvents(mask: RNBContext::event_read_thread_exiting); |
1170 | int err = ::pthread_create(newthread: &m_rx_pthread, NULL, |
1171 | start_routine: ThreadFunctionReadRemoteData, arg: this); |
1172 | if (err == 0) { |
1173 | // Our thread was successfully kicked off, wait for it to |
1174 | // set the started event so we can safely continue |
1175 | events.WaitForSetEvents(mask: RNBContext::event_read_thread_running); |
1176 | } else { |
1177 | events.ResetEvents(mask: RNBContext::event_read_thread_running); |
1178 | events.SetEvents(RNBContext::event_read_thread_exiting); |
1179 | } |
1180 | } |
1181 | } |
1182 | |
1183 | void RNBRemote::StopReadRemoteDataThread() { |
1184 | DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called" , |
1185 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
1186 | __FUNCTION__); |
1187 | PThreadEvent &events = m_ctx.Events(); |
1188 | if ((events.GetEventBits() & RNBContext::event_read_thread_running) == |
1189 | RNBContext::event_read_thread_running) { |
1190 | DNBLog("debugserver about to shut down packet communications to lldb." ); |
1191 | m_comm.Disconnect(save_errno: true); |
1192 | struct timespec timeout_abstime; |
1193 | DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0); |
1194 | |
1195 | // Wait for 2 seconds for the remote data thread to exit |
1196 | if (events.WaitForSetEvents(mask: RNBContext::event_read_thread_exiting, |
1197 | timeout_abstime: &timeout_abstime) == 0) { |
1198 | // Kill the remote data thread??? |
1199 | } |
1200 | } |
1201 | } |
1202 | |
1203 | void *RNBRemote::ThreadFunctionReadRemoteData(void *arg) { |
1204 | // Keep a shared pointer reference so this doesn't go away on us before the |
1205 | // thread is killed. |
1206 | DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread starting..." , |
1207 | __FUNCTION__, arg); |
1208 | RNBRemoteSP remoteSP(g_remoteSP); |
1209 | if (remoteSP.get() != NULL) { |
1210 | |
1211 | #if defined(__APPLE__) |
1212 | pthread_setname_np("read gdb-remote packets thread" ); |
1213 | #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) |
1214 | struct sched_param thread_param; |
1215 | int thread_sched_policy; |
1216 | if (pthread_getschedparam(pthread_self(), &thread_sched_policy, |
1217 | &thread_param) == 0) { |
1218 | thread_param.sched_priority = 47; |
1219 | pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); |
1220 | } |
1221 | #endif |
1222 | #endif |
1223 | |
1224 | RNBRemote *remote = remoteSP.get(); |
1225 | PThreadEvent &events = remote->Context().Events(); |
1226 | events.SetEvents(RNBContext::event_read_thread_running); |
1227 | // START: main receive remote command thread loop |
1228 | bool done = false; |
1229 | while (!done) { |
1230 | rnb_err_t err = remote->GetCommData(); |
1231 | |
1232 | switch (err) { |
1233 | case rnb_success: |
1234 | break; |
1235 | |
1236 | case rnb_err: |
1237 | DNBLogThreadedIf(LOG_RNB_REMOTE, |
1238 | "RNBSocket::GetCommData returned error %u" , err); |
1239 | done = true; |
1240 | break; |
1241 | |
1242 | case rnb_not_connected: |
1243 | DNBLogThreadedIf(LOG_RNB_REMOTE, |
1244 | "RNBSocket::GetCommData returned not connected..." ); |
1245 | done = true; |
1246 | break; |
1247 | } |
1248 | } |
1249 | // START: main receive remote command thread loop |
1250 | events.ResetEvents(mask: RNBContext::event_read_thread_running); |
1251 | events.SetEvents(RNBContext::event_read_thread_exiting); |
1252 | } |
1253 | DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread exiting..." , |
1254 | __FUNCTION__, arg); |
1255 | return NULL; |
1256 | } |
1257 | |
1258 | // If we fail to get back a valid CPU type for the remote process, |
1259 | // make a best guess for the CPU type based on the currently running |
1260 | // debugserver binary -- the debugger may not handle the case of an |
1261 | // un-specified process CPU type correctly. |
1262 | |
1263 | static cpu_type_t best_guess_cpu_type() { |
1264 | #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) |
1265 | if (sizeof(char *) == 8) { |
1266 | return CPU_TYPE_ARM64; |
1267 | } else { |
1268 | #if defined (__ARM64_ARCH_8_32__) |
1269 | return CPU_TYPE_ARM64_32; |
1270 | #endif |
1271 | return CPU_TYPE_ARM; |
1272 | } |
1273 | #elif defined(__i386__) || defined(__x86_64__) |
1274 | if (sizeof(char *) == 8) { |
1275 | return CPU_TYPE_X86_64; |
1276 | } else { |
1277 | return CPU_TYPE_I386; |
1278 | } |
1279 | #endif |
1280 | return 0; |
1281 | } |
1282 | |
1283 | /* Read the bytes in STR which are GDB Remote Protocol binary encoded bytes |
1284 | (8-bit bytes). |
1285 | This encoding uses 0x7d ('}') as an escape character for |
1286 | 0x7d ('}'), 0x23 ('#'), 0x24 ('$'), 0x2a ('*'). |
1287 | LEN is the number of bytes to be processed. If a character is escaped, |
1288 | it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte |
1289 | (end of string). */ |
1290 | |
1291 | std::vector<uint8_t> decode_binary_data(const char *str, size_t len) { |
1292 | std::vector<uint8_t> bytes; |
1293 | if (len == 0) { |
1294 | return bytes; |
1295 | } |
1296 | if (len == (size_t)-1) |
1297 | len = strlen(s: str); |
1298 | |
1299 | while (len--) { |
1300 | unsigned char c = *str++; |
1301 | if (c == 0x7d && len > 0) { |
1302 | len--; |
1303 | c = *str++ ^ 0x20; |
1304 | } |
1305 | bytes.push_back(x: c); |
1306 | } |
1307 | return bytes; |
1308 | } |
1309 | |
1310 | // Quote any meta characters in a std::string as per the binary |
1311 | // packet convention in the gdb-remote protocol. |
1312 | |
1313 | static std::string binary_encode_string(const std::string &s) { |
1314 | std::string output; |
1315 | const size_t s_size = s.size(); |
1316 | const char *s_chars = s.c_str(); |
1317 | |
1318 | for (size_t i = 0; i < s_size; i++) { |
1319 | unsigned char ch = *(s_chars + i); |
1320 | if (ch == '#' || ch == '$' || ch == '}' || ch == '*') { |
1321 | output.push_back(c: '}'); // 0x7d |
1322 | output.push_back(c: ch ^ 0x20); |
1323 | } else { |
1324 | output.push_back(c: ch); |
1325 | } |
1326 | } |
1327 | return output; |
1328 | } |
1329 | |
1330 | // If the value side of a key-value pair in JSON is a string, |
1331 | // and that string has a " character in it, the " character must |
1332 | // be escaped. |
1333 | |
1334 | std::string json_string_quote_metachars(const std::string &s) { |
1335 | if (s.find(c: '"') == std::string::npos) |
1336 | return s; |
1337 | |
1338 | std::string output; |
1339 | const size_t s_size = s.size(); |
1340 | const char *s_chars = s.c_str(); |
1341 | for (size_t i = 0; i < s_size; i++) { |
1342 | unsigned char ch = *(s_chars + i); |
1343 | if (ch == '"') { |
1344 | output.push_back(c: '\\'); |
1345 | } |
1346 | output.push_back(c: ch); |
1347 | } |
1348 | return output; |
1349 | } |
1350 | |
1351 | typedef struct register_map_entry { |
1352 | uint32_t debugserver_regnum; // debugserver register number |
1353 | uint32_t offset; // Offset in bytes into the register context data with no |
1354 | // padding between register values |
1355 | DNBRegisterInfo nub_info; // debugnub register info |
1356 | std::vector<uint32_t> value_regnums; |
1357 | std::vector<uint32_t> invalidate_regnums; |
1358 | } register_map_entry_t; |
1359 | |
1360 | // If the notion of registers differs from what is handed out by the |
1361 | // architecture, then flavors can be defined here. |
1362 | |
1363 | static std::vector<register_map_entry_t> g_dynamic_register_map; |
1364 | static register_map_entry_t *g_reg_entries = NULL; |
1365 | static size_t g_num_reg_entries = 0; |
1366 | |
1367 | void RNBRemote::Initialize() { DNBInitialize(); } |
1368 | |
1369 | bool RNBRemote::InitializeRegisters(bool force) { |
1370 | pid_t pid = m_ctx.ProcessID(); |
1371 | if (pid == INVALID_NUB_PROCESS) |
1372 | return false; |
1373 | |
1374 | DNBLogThreadedIf( |
1375 | LOG_RNB_PROC, |
1376 | "RNBRemote::%s() getting native registers from DNB interface" , |
1377 | __FUNCTION__); |
1378 | // Discover the registers by querying the DNB interface and letting it |
1379 | // state the registers that it would like to export. This allows the |
1380 | // registers to be discovered using multiple qRegisterInfo calls to get |
1381 | // all register information after the architecture for the process is |
1382 | // determined. |
1383 | if (force) { |
1384 | g_dynamic_register_map.clear(); |
1385 | g_reg_entries = NULL; |
1386 | g_num_reg_entries = 0; |
1387 | } |
1388 | |
1389 | if (g_dynamic_register_map.empty()) { |
1390 | nub_size_t num_reg_sets = 0; |
1391 | const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(num_reg_sets: &num_reg_sets); |
1392 | |
1393 | assert(num_reg_sets > 0 && reg_sets != NULL); |
1394 | |
1395 | uint32_t regnum = 0; |
1396 | uint32_t reg_data_offset = 0; |
1397 | typedef std::map<std::string, uint32_t> NameToRegNum; |
1398 | NameToRegNum name_to_regnum; |
1399 | for (nub_size_t set = 0; set < num_reg_sets; ++set) { |
1400 | if (reg_sets[set].registers == NULL) |
1401 | continue; |
1402 | |
1403 | for (uint32_t reg = 0; reg < reg_sets[set].num_registers; ++reg) { |
1404 | register_map_entry_t reg_entry = { |
1405 | .debugserver_regnum: regnum++, // register number starts at zero and goes up with no gaps |
1406 | .offset: reg_data_offset, // Offset into register context data, no gaps |
1407 | // between registers |
1408 | .nub_info: reg_sets[set].registers[reg], // DNBRegisterInfo |
1409 | .value_regnums: {}, |
1410 | .invalidate_regnums: {}, |
1411 | }; |
1412 | |
1413 | name_to_regnum[reg_entry.nub_info.name] = reg_entry.debugserver_regnum; |
1414 | |
1415 | if (reg_entry.nub_info.value_regs == NULL) { |
1416 | reg_data_offset += reg_entry.nub_info.size; |
1417 | } |
1418 | |
1419 | g_dynamic_register_map.push_back(x: reg_entry); |
1420 | } |
1421 | } |
1422 | |
1423 | // Now we must find any registers whose values are in other registers and |
1424 | // fix up |
1425 | // the offsets since we removed all gaps... |
1426 | for (auto ®_entry : g_dynamic_register_map) { |
1427 | if (reg_entry.nub_info.value_regs) { |
1428 | uint32_t new_offset = UINT32_MAX; |
1429 | for (size_t i = 0; reg_entry.nub_info.value_regs[i] != NULL; ++i) { |
1430 | const char *name = reg_entry.nub_info.value_regs[i]; |
1431 | auto pos = name_to_regnum.find(x: name); |
1432 | if (pos != name_to_regnum.end()) { |
1433 | regnum = pos->second; |
1434 | reg_entry.value_regnums.push_back(x: regnum); |
1435 | if (regnum < g_dynamic_register_map.size()) { |
1436 | // The offset for value_regs registers is the offset within the |
1437 | // register with the lowest offset |
1438 | const uint32_t reg_offset = |
1439 | g_dynamic_register_map[regnum].offset + |
1440 | reg_entry.nub_info.offset; |
1441 | if (new_offset > reg_offset) |
1442 | new_offset = reg_offset; |
1443 | } |
1444 | } |
1445 | } |
1446 | |
1447 | if (new_offset != UINT32_MAX) { |
1448 | reg_entry.offset = new_offset; |
1449 | } else { |
1450 | DNBLogThreaded("no offset was calculated entry for register %s" , |
1451 | reg_entry.nub_info.name); |
1452 | reg_entry.offset = UINT32_MAX; |
1453 | } |
1454 | } |
1455 | |
1456 | if (reg_entry.nub_info.update_regs) { |
1457 | for (size_t i = 0; reg_entry.nub_info.update_regs[i] != NULL; ++i) { |
1458 | const char *name = reg_entry.nub_info.update_regs[i]; |
1459 | auto pos = name_to_regnum.find(x: name); |
1460 | if (pos != name_to_regnum.end()) { |
1461 | regnum = pos->second; |
1462 | reg_entry.invalidate_regnums.push_back(x: regnum); |
1463 | } |
1464 | } |
1465 | } |
1466 | } |
1467 | |
1468 | // for (auto ®_entry: g_dynamic_register_map) |
1469 | // { |
1470 | // DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s", |
1471 | // reg_entry.offset, |
1472 | // reg_entry.nub_info.size, |
1473 | // reg_entry.nub_info.value_regs != NULL, |
1474 | // reg_entry.nub_info.name); |
1475 | // } |
1476 | |
1477 | g_reg_entries = g_dynamic_register_map.data(); |
1478 | g_num_reg_entries = g_dynamic_register_map.size(); |
1479 | } |
1480 | return true; |
1481 | } |
1482 | |
1483 | /* The inferior has stopped executing; send a packet |
1484 | to gdb to let it know. */ |
1485 | |
1486 | void RNBRemote::NotifyThatProcessStopped(void) { |
1487 | RNBRemote::HandlePacket_last_signal(NULL); |
1488 | return; |
1489 | } |
1490 | |
1491 | /* 'A arglen,argnum,arg,...' |
1492 | Update the inferior context CTX with the program name and arg |
1493 | list. |
1494 | The documentation for this packet is underwhelming but my best reading |
1495 | of this is that it is a series of (len, position #, arg)'s, one for |
1496 | each argument with "arg" hex encoded (two 0-9a-f chars?). |
1497 | Why we need BOTH a "len" and a hex encoded "arg" is beyond me - either |
1498 | is sufficient to get around the "," position separator escape issue. |
1499 | |
1500 | e.g. our best guess for a valid 'A' packet for "gdb -q a.out" is |
1501 | |
1502 | 6,0,676462,4,1,2d71,10,2,612e6f7574 |
1503 | |
1504 | Note that "argnum" and "arglen" are numbers in base 10. Again, that's |
1505 | not documented either way but I'm assuming it's so. */ |
1506 | |
1507 | rnb_err_t RNBRemote::HandlePacket_A(const char *p) { |
1508 | if (p == NULL || *p == '\0') { |
1509 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1510 | description: "Null packet for 'A' pkt" ); |
1511 | } |
1512 | p++; |
1513 | if (*p == '\0' || !isdigit(*p)) { |
1514 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1515 | description: "arglen not specified on 'A' pkt" ); |
1516 | } |
1517 | |
1518 | /* I promise I don't modify it anywhere in this function. strtoul()'s |
1519 | 2nd arg has to be non-const which makes it problematic to step |
1520 | through the string easily. */ |
1521 | char *buf = const_cast<char *>(p); |
1522 | |
1523 | RNBContext &ctx = Context(); |
1524 | |
1525 | while (*buf != '\0') { |
1526 | unsigned long arglen, argnum; |
1527 | std::string arg; |
1528 | char *c; |
1529 | |
1530 | errno = 0; |
1531 | arglen = strtoul(nptr: buf, endptr: &c, base: 10); |
1532 | if (errno != 0 && arglen == 0) { |
1533 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1534 | description: "arglen not a number on 'A' pkt" ); |
1535 | } |
1536 | if (*c != ',') { |
1537 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1538 | description: "arglen not followed by comma on 'A' pkt" ); |
1539 | } |
1540 | buf = c + 1; |
1541 | |
1542 | errno = 0; |
1543 | argnum = strtoul(nptr: buf, endptr: &c, base: 10); |
1544 | if (errno != 0 && argnum == 0) { |
1545 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1546 | description: "argnum not a number on 'A' pkt" ); |
1547 | } |
1548 | if (*c != ',') { |
1549 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1550 | description: "arglen not followed by comma on 'A' pkt" ); |
1551 | } |
1552 | buf = c + 1; |
1553 | |
1554 | c = buf; |
1555 | buf = buf + arglen; |
1556 | while (c < buf && *c != '\0' && c + 1 < buf && *(c + 1) != '\0') { |
1557 | char smallbuf[3]; |
1558 | smallbuf[0] = *c; |
1559 | smallbuf[1] = *(c + 1); |
1560 | smallbuf[2] = '\0'; |
1561 | |
1562 | errno = 0; |
1563 | int ch = static_cast<int>(strtoul(nptr: smallbuf, NULL, base: 16)); |
1564 | if (errno != 0 && ch == 0) { |
1565 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1566 | description: "non-hex char in arg on 'A' pkt" ); |
1567 | } |
1568 | |
1569 | arg.push_back(c: ch); |
1570 | c += 2; |
1571 | } |
1572 | |
1573 | ctx.PushArgument(arg: arg.c_str()); |
1574 | if (*buf == ',') |
1575 | buf++; |
1576 | } |
1577 | SendPacket(s: "OK" ); |
1578 | |
1579 | return rnb_success; |
1580 | } |
1581 | |
1582 | /* 'H c t' |
1583 | Set the thread for subsequent actions; 'c' for step/continue ops, |
1584 | 'g' for other ops. -1 means all threads, 0 means any thread. */ |
1585 | |
1586 | rnb_err_t RNBRemote::HandlePacket_H(const char *p) { |
1587 | p++; // skip 'H' |
1588 | if (*p != 'c' && *p != 'g') { |
1589 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1590 | description: "Missing 'c' or 'g' type in H packet" ); |
1591 | } |
1592 | |
1593 | if (!m_ctx.HasValidProcessID()) { |
1594 | // We allow gdb to connect to a server that hasn't started running |
1595 | // the target yet. gdb still wants to ask questions about it and |
1596 | // freaks out if it gets an error. So just return OK here. |
1597 | } |
1598 | |
1599 | errno = 0; |
1600 | nub_thread_t tid = strtoul(nptr: p + 1, NULL, base: 16); |
1601 | if (errno != 0 && tid == 0) { |
1602 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1603 | description: "Invalid thread number in H packet" ); |
1604 | } |
1605 | if (*p == 'c') |
1606 | SetContinueThread(tid); |
1607 | if (*p == 'g') |
1608 | SetCurrentThread(tid); |
1609 | |
1610 | return SendPacket(s: "OK" ); |
1611 | } |
1612 | |
1613 | rnb_err_t RNBRemote::HandlePacket_qLaunchSuccess(const char *p) { |
1614 | if (m_ctx.HasValidProcessID() || m_ctx.LaunchStatus().Status() == 0) |
1615 | return SendPacket(s: "OK" ); |
1616 | std::string status_str; |
1617 | return SendErrorPacket("E89" , m_ctx.LaunchStatusAsString(status_str)); |
1618 | } |
1619 | |
1620 | rnb_err_t RNBRemote::HandlePacket_qShlibInfoAddr(const char *p) { |
1621 | if (m_ctx.HasValidProcessID()) { |
1622 | nub_addr_t shlib_info_addr = |
1623 | DNBProcessGetSharedLibraryInfoAddress(m_ctx.ProcessID()); |
1624 | if (shlib_info_addr != INVALID_NUB_ADDRESS) { |
1625 | std::ostringstream ostrm; |
1626 | ostrm << RAW_HEXBASE << shlib_info_addr; |
1627 | return SendPacket(s: ostrm.str()); |
1628 | } |
1629 | } |
1630 | return SendErrorPacket(errcode: "E44" ); |
1631 | } |
1632 | |
1633 | rnb_err_t RNBRemote::HandlePacket_qStepPacketSupported(const char *p) { |
1634 | // Normally the "s" packet is mandatory, yet in gdb when using ARM, they |
1635 | // get around the need for this packet by implementing software single |
1636 | // stepping from gdb. Current versions of debugserver do support the "s" |
1637 | // packet, yet some older versions do not. We need a way to tell if this |
1638 | // packet is supported so we can disable software single stepping in gdb |
1639 | // for remote targets (so the "s" packet will get used). |
1640 | return SendPacket(s: "OK" ); |
1641 | } |
1642 | |
1643 | rnb_err_t RNBRemote::HandlePacket_qSyncThreadStateSupported(const char *p) { |
1644 | // We support attachOrWait meaning attach if the process exists, otherwise |
1645 | // wait to attach. |
1646 | return SendPacket(s: "OK" ); |
1647 | } |
1648 | |
1649 | rnb_err_t RNBRemote::HandlePacket_qVAttachOrWaitSupported(const char *p) { |
1650 | // We support attachOrWait meaning attach if the process exists, otherwise |
1651 | // wait to attach. |
1652 | return SendPacket(s: "OK" ); |
1653 | } |
1654 | |
1655 | rnb_err_t RNBRemote::HandlePacket_qThreadStopInfo(const char *p) { |
1656 | p += strlen(s: "qThreadStopInfo" ); |
1657 | nub_thread_t tid = strtoul(nptr: p, endptr: 0, base: 16); |
1658 | return SendStopReplyPacketForThread(tid); |
1659 | } |
1660 | |
1661 | rnb_err_t RNBRemote::HandlePacket_qThreadInfo(const char *p) { |
1662 | // We allow gdb to connect to a server that hasn't started running |
1663 | // the target yet. gdb still wants to ask questions about it and |
1664 | // freaks out if it gets an error. So just return OK here. |
1665 | nub_process_t pid = m_ctx.ProcessID(); |
1666 | if (pid == INVALID_NUB_PROCESS) |
1667 | return SendPacket(s: "OK" ); |
1668 | |
1669 | // Only "qfThreadInfo" and "qsThreadInfo" get into this function so |
1670 | // we only need to check the second byte to tell which is which |
1671 | if (p[1] == 'f') { |
1672 | nub_size_t numthreads = DNBProcessGetNumThreads(pid); |
1673 | std::ostringstream ostrm; |
1674 | ostrm << "m" ; |
1675 | bool first = true; |
1676 | for (nub_size_t i = 0; i < numthreads; ++i) { |
1677 | if (first) |
1678 | first = false; |
1679 | else |
1680 | ostrm << "," ; |
1681 | nub_thread_t th = DNBProcessGetThreadAtIndex(pid, thread_idx: i); |
1682 | ostrm << std::hex << th; |
1683 | } |
1684 | return SendPacket(s: ostrm.str()); |
1685 | } else { |
1686 | return SendPacket(s: "l" ); |
1687 | } |
1688 | } |
1689 | |
1690 | rnb_err_t RNBRemote::HandlePacket_qThreadExtraInfo(const char *p) { |
1691 | // We allow gdb to connect to a server that hasn't started running |
1692 | // the target yet. gdb still wants to ask questions about it and |
1693 | // freaks out if it gets an error. So just return OK here. |
1694 | nub_process_t pid = m_ctx.ProcessID(); |
1695 | if (pid == INVALID_NUB_PROCESS) |
1696 | return SendPacket(s: "OK" ); |
1697 | |
1698 | /* This is supposed to return a string like 'Runnable' or |
1699 | 'Blocked on Mutex'. |
1700 | The returned string is formatted like the "A" packet - a |
1701 | sequence of letters encoded in as 2-hex-chars-per-letter. */ |
1702 | p += strlen(s: "qThreadExtraInfo" ); |
1703 | if (*p++ != ',') |
1704 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1705 | description: "Illformed qThreadExtraInfo packet" ); |
1706 | errno = 0; |
1707 | nub_thread_t tid = strtoul(nptr: p, NULL, base: 16); |
1708 | if (errno != 0 && tid == 0) { |
1709 | return HandlePacket_ILLFORMED( |
1710 | __FILE__, __LINE__, p, |
1711 | description: "Invalid thread number in qThreadExtraInfo packet" ); |
1712 | } |
1713 | |
1714 | const char *threadInfo = DNBThreadGetInfo(pid, tid); |
1715 | if (threadInfo != NULL && threadInfo[0]) { |
1716 | return SendHexEncodedBytePacket(NULL, buf: threadInfo, buf_len: strlen(s: threadInfo), NULL); |
1717 | } else { |
1718 | // "OK" == 4f6b |
1719 | // Return "OK" as a ASCII hex byte stream if things go wrong |
1720 | return SendPacket(s: "4f6b" ); |
1721 | } |
1722 | |
1723 | return SendPacket(s: "" ); |
1724 | } |
1725 | |
1726 | const char *k_space_delimiters = " \t" ; |
1727 | static void skip_spaces(std::string &line) { |
1728 | if (!line.empty()) { |
1729 | size_t space_pos = line.find_first_not_of(s: k_space_delimiters); |
1730 | if (space_pos > 0) |
1731 | line.erase(pos: 0, n: space_pos); |
1732 | } |
1733 | } |
1734 | |
1735 | static std::string get_identifier(std::string &line) { |
1736 | std::string word; |
1737 | skip_spaces(line); |
1738 | const size_t line_size = line.size(); |
1739 | size_t end_pos; |
1740 | for (end_pos = 0; end_pos < line_size; ++end_pos) { |
1741 | if (end_pos == 0) { |
1742 | if (isalpha(line[end_pos]) || line[end_pos] == '_') |
1743 | continue; |
1744 | } else if (isalnum(line[end_pos]) || line[end_pos] == '_') |
1745 | continue; |
1746 | break; |
1747 | } |
1748 | word.assign(str: line, pos: 0, n: end_pos); |
1749 | line.erase(pos: 0, n: end_pos); |
1750 | return word; |
1751 | } |
1752 | |
1753 | static std::string get_operator(std::string &line) { |
1754 | std::string op; |
1755 | skip_spaces(line); |
1756 | if (!line.empty()) { |
1757 | if (line[0] == '=') { |
1758 | op = '='; |
1759 | line.erase(pos: 0, n: 1); |
1760 | } |
1761 | } |
1762 | return op; |
1763 | } |
1764 | |
1765 | static std::string get_value(std::string &line) { |
1766 | std::string value; |
1767 | skip_spaces(line); |
1768 | if (!line.empty()) { |
1769 | value.swap(s&: line); |
1770 | } |
1771 | return value; |
1772 | } |
1773 | |
1774 | extern void FileLogCallback(void *baton, uint32_t flags, const char *format, |
1775 | va_list args); |
1776 | extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, |
1777 | va_list args); |
1778 | |
1779 | rnb_err_t RNBRemote::HandlePacket_qRcmd(const char *p) { |
1780 | const char *c = p + strlen(s: "qRcmd," ); |
1781 | std::string line; |
1782 | while (c[0] && c[1]) { |
1783 | char smallbuf[3] = {c[0], c[1], '\0'}; |
1784 | errno = 0; |
1785 | int ch = static_cast<int>(strtoul(nptr: smallbuf, NULL, base: 16)); |
1786 | if (errno != 0 && ch == 0) |
1787 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
1788 | description: "non-hex char in payload of qRcmd packet" ); |
1789 | line.push_back(c: ch); |
1790 | c += 2; |
1791 | } |
1792 | if (*c == '\0') { |
1793 | std::string command = get_identifier(line); |
1794 | if (command == "set" ) { |
1795 | std::string variable = get_identifier(line); |
1796 | std::string op = get_operator(line); |
1797 | std::string value = get_value(line); |
1798 | if (variable == "logfile" ) { |
1799 | FILE *log_file = fopen(filename: value.c_str(), modes: "w" ); |
1800 | if (log_file) { |
1801 | DNBLogSetLogCallback(callback: FileLogCallback, baton: log_file); |
1802 | return SendPacket(s: "OK" ); |
1803 | } |
1804 | return SendErrorPacket(errcode: "E71" ); |
1805 | } else if (variable == "logmask" ) { |
1806 | char *end; |
1807 | errno = 0; |
1808 | uint32_t logmask = |
1809 | static_cast<uint32_t>(strtoul(nptr: value.c_str(), endptr: &end, base: 0)); |
1810 | if (errno == 0 && end && *end == '\0') { |
1811 | DNBLogSetLogMask(mask: logmask); |
1812 | if (!DNBLogGetLogCallback()) |
1813 | DNBLogSetLogCallback(callback: ASLLogCallback, NULL); |
1814 | return SendPacket(s: "OK" ); |
1815 | } |
1816 | errno = 0; |
1817 | logmask = static_cast<uint32_t>(strtoul(nptr: value.c_str(), endptr: &end, base: 16)); |
1818 | if (errno == 0 && end && *end == '\0') { |
1819 | DNBLogSetLogMask(mask: logmask); |
1820 | return SendPacket(s: "OK" ); |
1821 | } |
1822 | return SendErrorPacket(errcode: "E72" ); |
1823 | } |
1824 | return SendErrorPacket(errcode: "E70" ); |
1825 | } |
1826 | return SendErrorPacket(errcode: "E69" ); |
1827 | } |
1828 | return SendErrorPacket(errcode: "E73" ); |
1829 | } |
1830 | |
1831 | rnb_err_t RNBRemote::HandlePacket_qC(const char *p) { |
1832 | nub_thread_t tid; |
1833 | std::ostringstream rep; |
1834 | // If we haven't run the process yet, we tell the debugger the |
1835 | // pid is 0. That way it can know to tell use to run later on. |
1836 | if (!m_ctx.HasValidProcessID()) |
1837 | tid = 0; |
1838 | else { |
1839 | // Grab the current thread. |
1840 | tid = DNBProcessGetCurrentThread(m_ctx.ProcessID()); |
1841 | // Make sure we set the current thread so g and p packets return |
1842 | // the data the gdb will expect. |
1843 | SetCurrentThread(tid); |
1844 | } |
1845 | rep << "QC" << std::hex << tid; |
1846 | return SendPacket(s: rep.str()); |
1847 | } |
1848 | |
1849 | rnb_err_t RNBRemote::HandlePacket_qEcho(const char *p) { |
1850 | // Just send the exact same packet back that we received to |
1851 | // synchronize the response packets after a previous packet |
1852 | // timed out. This allows the debugger to get back on track |
1853 | // with responses after a packet timeout. |
1854 | return SendPacket(s: p); |
1855 | } |
1856 | |
1857 | rnb_err_t RNBRemote::HandlePacket_qGetPid(const char *p) { |
1858 | nub_process_t pid; |
1859 | std::ostringstream rep; |
1860 | // If we haven't run the process yet, we tell the debugger the |
1861 | // pid is 0. That way it can know to tell use to run later on. |
1862 | if (m_ctx.HasValidProcessID()) |
1863 | pid = m_ctx.ProcessID(); |
1864 | else |
1865 | pid = 0; |
1866 | rep << std::hex << pid; |
1867 | return SendPacket(s: rep.str()); |
1868 | } |
1869 | |
1870 | rnb_err_t RNBRemote::HandlePacket_qRegisterInfo(const char *p) { |
1871 | if (g_num_reg_entries == 0) |
1872 | InitializeRegisters(); |
1873 | |
1874 | p += strlen(s: "qRegisterInfo" ); |
1875 | |
1876 | nub_size_t num_reg_sets = 0; |
1877 | const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo(num_reg_sets: &num_reg_sets); |
1878 | uint32_t reg_num = static_cast<uint32_t>(strtoul(nptr: p, endptr: 0, base: 16)); |
1879 | |
1880 | if (reg_num < g_num_reg_entries) { |
1881 | const register_map_entry_t *reg_entry = &g_reg_entries[reg_num]; |
1882 | std::ostringstream ostrm; |
1883 | if (reg_entry->nub_info.name) |
1884 | ostrm << "name:" << reg_entry->nub_info.name << ';'; |
1885 | if (reg_entry->nub_info.alt) |
1886 | ostrm << "alt-name:" << reg_entry->nub_info.alt << ';'; |
1887 | |
1888 | ostrm << "bitsize:" << std::dec << reg_entry->nub_info.size * 8 << ';'; |
1889 | ostrm << "offset:" << std::dec << reg_entry->offset << ';'; |
1890 | |
1891 | switch (reg_entry->nub_info.type) { |
1892 | case Uint: |
1893 | ostrm << "encoding:uint;" ; |
1894 | break; |
1895 | case Sint: |
1896 | ostrm << "encoding:sint;" ; |
1897 | break; |
1898 | case IEEE754: |
1899 | ostrm << "encoding:ieee754;" ; |
1900 | break; |
1901 | case Vector: |
1902 | ostrm << "encoding:vector;" ; |
1903 | break; |
1904 | } |
1905 | |
1906 | switch (reg_entry->nub_info.format) { |
1907 | case Binary: |
1908 | ostrm << "format:binary;" ; |
1909 | break; |
1910 | case Decimal: |
1911 | ostrm << "format:decimal;" ; |
1912 | break; |
1913 | case Hex: |
1914 | ostrm << "format:hex;" ; |
1915 | break; |
1916 | case Float: |
1917 | ostrm << "format:float;" ; |
1918 | break; |
1919 | case VectorOfSInt8: |
1920 | ostrm << "format:vector-sint8;" ; |
1921 | break; |
1922 | case VectorOfUInt8: |
1923 | ostrm << "format:vector-uint8;" ; |
1924 | break; |
1925 | case VectorOfSInt16: |
1926 | ostrm << "format:vector-sint16;" ; |
1927 | break; |
1928 | case VectorOfUInt16: |
1929 | ostrm << "format:vector-uint16;" ; |
1930 | break; |
1931 | case VectorOfSInt32: |
1932 | ostrm << "format:vector-sint32;" ; |
1933 | break; |
1934 | case VectorOfUInt32: |
1935 | ostrm << "format:vector-uint32;" ; |
1936 | break; |
1937 | case VectorOfFloat32: |
1938 | ostrm << "format:vector-float32;" ; |
1939 | break; |
1940 | case VectorOfUInt128: |
1941 | ostrm << "format:vector-uint128;" ; |
1942 | break; |
1943 | }; |
1944 | |
1945 | if (reg_set_info && reg_entry->nub_info.set < num_reg_sets) |
1946 | ostrm << "set:" << reg_set_info[reg_entry->nub_info.set].name << ';'; |
1947 | |
1948 | if (reg_entry->nub_info.reg_ehframe != INVALID_NUB_REGNUM) |
1949 | ostrm << "ehframe:" << std::dec << reg_entry->nub_info.reg_ehframe << ';'; |
1950 | |
1951 | if (reg_entry->nub_info.reg_dwarf != INVALID_NUB_REGNUM) |
1952 | ostrm << "dwarf:" << std::dec << reg_entry->nub_info.reg_dwarf << ';'; |
1953 | |
1954 | switch (reg_entry->nub_info.reg_generic) { |
1955 | case GENERIC_REGNUM_FP: |
1956 | ostrm << "generic:fp;" ; |
1957 | break; |
1958 | case GENERIC_REGNUM_PC: |
1959 | ostrm << "generic:pc;" ; |
1960 | break; |
1961 | case GENERIC_REGNUM_SP: |
1962 | ostrm << "generic:sp;" ; |
1963 | break; |
1964 | case GENERIC_REGNUM_RA: |
1965 | ostrm << "generic:ra;" ; |
1966 | break; |
1967 | case GENERIC_REGNUM_FLAGS: |
1968 | ostrm << "generic:flags;" ; |
1969 | break; |
1970 | case GENERIC_REGNUM_ARG1: |
1971 | ostrm << "generic:arg1;" ; |
1972 | break; |
1973 | case GENERIC_REGNUM_ARG2: |
1974 | ostrm << "generic:arg2;" ; |
1975 | break; |
1976 | case GENERIC_REGNUM_ARG3: |
1977 | ostrm << "generic:arg3;" ; |
1978 | break; |
1979 | case GENERIC_REGNUM_ARG4: |
1980 | ostrm << "generic:arg4;" ; |
1981 | break; |
1982 | case GENERIC_REGNUM_ARG5: |
1983 | ostrm << "generic:arg5;" ; |
1984 | break; |
1985 | case GENERIC_REGNUM_ARG6: |
1986 | ostrm << "generic:arg6;" ; |
1987 | break; |
1988 | case GENERIC_REGNUM_ARG7: |
1989 | ostrm << "generic:arg7;" ; |
1990 | break; |
1991 | case GENERIC_REGNUM_ARG8: |
1992 | ostrm << "generic:arg8;" ; |
1993 | break; |
1994 | default: |
1995 | break; |
1996 | } |
1997 | |
1998 | if (!reg_entry->value_regnums.empty()) { |
1999 | ostrm << "container-regs:" ; |
2000 | for (size_t i = 0, n = reg_entry->value_regnums.size(); i < n; ++i) { |
2001 | if (i > 0) |
2002 | ostrm << ','; |
2003 | ostrm << RAW_HEXBASE << reg_entry->value_regnums[i]; |
2004 | } |
2005 | ostrm << ';'; |
2006 | } |
2007 | |
2008 | if (!reg_entry->invalidate_regnums.empty()) { |
2009 | ostrm << "invalidate-regs:" ; |
2010 | for (size_t i = 0, n = reg_entry->invalidate_regnums.size(); i < n; ++i) { |
2011 | if (i > 0) |
2012 | ostrm << ','; |
2013 | ostrm << RAW_HEXBASE << reg_entry->invalidate_regnums[i]; |
2014 | } |
2015 | ostrm << ';'; |
2016 | } |
2017 | |
2018 | return SendPacket(s: ostrm.str()); |
2019 | } |
2020 | return SendErrorPacket(errcode: "E45" ); |
2021 | } |
2022 | |
2023 | /* This expects a packet formatted like |
2024 | |
2025 | QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE; |
2026 | |
2027 | with the "QSetLogging:" already removed from the start. Maybe in the |
2028 | future this packet will include other keyvalue pairs like |
2029 | |
2030 | QSetLogging:bitmask=LOG_ALL;mode=asl; |
2031 | */ |
2032 | |
2033 | rnb_err_t set_logging(const char *p) { |
2034 | int bitmask = 0; |
2035 | while (p && *p != '\0') { |
2036 | if (strncmp(s1: p, s2: "bitmask=" , n: sizeof("bitmask=" ) - 1) == 0) { |
2037 | p += sizeof("bitmask=" ) - 1; |
2038 | while (p && *p != '\0' && *p != ';') { |
2039 | if (*p == '|') |
2040 | p++; |
2041 | |
2042 | // to regenerate the LOG_ entries (not including the LOG_RNB entries) |
2043 | // $ for logname in `grep '^#define LOG_' DNBDefs.h | egrep -v |
2044 | // 'LOG_HI|LOG_LO' | awk '{print $2}'` |
2045 | // do |
2046 | // echo " else if (strncmp (p, \"$logname\", sizeof |
2047 | // (\"$logname\") - 1) == 0)" |
2048 | // echo " {" |
2049 | // echo " p += sizeof (\"$logname\") - 1;" |
2050 | // echo " bitmask |= $logname;" |
2051 | // echo " }" |
2052 | // done |
2053 | if (strncmp(s1: p, s2: "LOG_VERBOSE" , n: sizeof("LOG_VERBOSE" ) - 1) == 0) { |
2054 | p += sizeof("LOG_VERBOSE" ) - 1; |
2055 | bitmask |= LOG_VERBOSE; |
2056 | } else if (strncmp(s1: p, s2: "LOG_PROCESS" , n: sizeof("LOG_PROCESS" ) - 1) == 0) { |
2057 | p += sizeof("LOG_PROCESS" ) - 1; |
2058 | bitmask |= LOG_PROCESS; |
2059 | } else if (strncmp(s1: p, s2: "LOG_THREAD" , n: sizeof("LOG_THREAD" ) - 1) == 0) { |
2060 | p += sizeof("LOG_THREAD" ) - 1; |
2061 | bitmask |= LOG_THREAD; |
2062 | } else if (strncmp(s1: p, s2: "LOG_EXCEPTIONS" , n: sizeof("LOG_EXCEPTIONS" ) - 1) == |
2063 | 0) { |
2064 | p += sizeof("LOG_EXCEPTIONS" ) - 1; |
2065 | bitmask |= LOG_EXCEPTIONS; |
2066 | } else if (strncmp(s1: p, s2: "LOG_SHLIB" , n: sizeof("LOG_SHLIB" ) - 1) == 0) { |
2067 | p += sizeof("LOG_SHLIB" ) - 1; |
2068 | bitmask |= LOG_SHLIB; |
2069 | } else if (strncmp(s1: p, s2: "LOG_MEMORY_DATA_SHORT" , |
2070 | n: sizeof("LOG_MEMORY_DATA_SHORT" ) - 1) == 0) { |
2071 | p += sizeof("LOG_MEMORY_DATA_SHORT" ) - 1; |
2072 | bitmask |= LOG_MEMORY_DATA_SHORT; |
2073 | } else if (strncmp(s1: p, s2: "LOG_MEMORY_DATA_LONG" , |
2074 | n: sizeof("LOG_MEMORY_DATA_LONG" ) - 1) == 0) { |
2075 | p += sizeof("LOG_MEMORY_DATA_LONG" ) - 1; |
2076 | bitmask |= LOG_MEMORY_DATA_LONG; |
2077 | } else if (strncmp(s1: p, s2: "LOG_MEMORY_PROTECTIONS" , |
2078 | n: sizeof("LOG_MEMORY_PROTECTIONS" ) - 1) == 0) { |
2079 | p += sizeof("LOG_MEMORY_PROTECTIONS" ) - 1; |
2080 | bitmask |= LOG_MEMORY_PROTECTIONS; |
2081 | } else if (strncmp(s1: p, s2: "LOG_MEMORY" , n: sizeof("LOG_MEMORY" ) - 1) == 0) { |
2082 | p += sizeof("LOG_MEMORY" ) - 1; |
2083 | bitmask |= LOG_MEMORY; |
2084 | } else if (strncmp(s1: p, s2: "LOG_BREAKPOINTS" , |
2085 | n: sizeof("LOG_BREAKPOINTS" ) - 1) == 0) { |
2086 | p += sizeof("LOG_BREAKPOINTS" ) - 1; |
2087 | bitmask |= LOG_BREAKPOINTS; |
2088 | } else if (strncmp(s1: p, s2: "LOG_EVENTS" , n: sizeof("LOG_EVENTS" ) - 1) == 0) { |
2089 | p += sizeof("LOG_EVENTS" ) - 1; |
2090 | bitmask |= LOG_EVENTS; |
2091 | } else if (strncmp(s1: p, s2: "LOG_WATCHPOINTS" , |
2092 | n: sizeof("LOG_WATCHPOINTS" ) - 1) == 0) { |
2093 | p += sizeof("LOG_WATCHPOINTS" ) - 1; |
2094 | bitmask |= LOG_WATCHPOINTS; |
2095 | } else if (strncmp(s1: p, s2: "LOG_STEP" , n: sizeof("LOG_STEP" ) - 1) == 0) { |
2096 | p += sizeof("LOG_STEP" ) - 1; |
2097 | bitmask |= LOG_STEP; |
2098 | } else if (strncmp(s1: p, s2: "LOG_TASK" , n: sizeof("LOG_TASK" ) - 1) == 0) { |
2099 | p += sizeof("LOG_TASK" ) - 1; |
2100 | bitmask |= LOG_TASK; |
2101 | } else if (strncmp(s1: p, s2: "LOG_ALL" , n: sizeof("LOG_ALL" ) - 1) == 0) { |
2102 | p += sizeof("LOG_ALL" ) - 1; |
2103 | bitmask |= LOG_ALL; |
2104 | } else if (strncmp(s1: p, s2: "LOG_DEFAULT" , n: sizeof("LOG_DEFAULT" ) - 1) == 0) { |
2105 | p += sizeof("LOG_DEFAULT" ) - 1; |
2106 | bitmask |= LOG_DEFAULT; |
2107 | } |
2108 | // end of auto-generated entries |
2109 | |
2110 | else if (strncmp(s1: p, s2: "LOG_NONE" , n: sizeof("LOG_NONE" ) - 1) == 0) { |
2111 | p += sizeof("LOG_NONE" ) - 1; |
2112 | bitmask = 0; |
2113 | } else if (strncmp(s1: p, s2: "LOG_RNB_MINIMAL" , |
2114 | n: sizeof("LOG_RNB_MINIMAL" ) - 1) == 0) { |
2115 | p += sizeof("LOG_RNB_MINIMAL" ) - 1; |
2116 | bitmask |= LOG_RNB_MINIMAL; |
2117 | } else if (strncmp(s1: p, s2: "LOG_RNB_MEDIUM" , n: sizeof("LOG_RNB_MEDIUM" ) - 1) == |
2118 | 0) { |
2119 | p += sizeof("LOG_RNB_MEDIUM" ) - 1; |
2120 | bitmask |= LOG_RNB_MEDIUM; |
2121 | } else if (strncmp(s1: p, s2: "LOG_RNB_MAX" , n: sizeof("LOG_RNB_MAX" ) - 1) == 0) { |
2122 | p += sizeof("LOG_RNB_MAX" ) - 1; |
2123 | bitmask |= LOG_RNB_MAX; |
2124 | } else if (strncmp(s1: p, s2: "LOG_RNB_COMM" , n: sizeof("LOG_RNB_COMM" ) - 1) == |
2125 | 0) { |
2126 | p += sizeof("LOG_RNB_COMM" ) - 1; |
2127 | bitmask |= LOG_RNB_COMM; |
2128 | } else if (strncmp(s1: p, s2: "LOG_RNB_REMOTE" , n: sizeof("LOG_RNB_REMOTE" ) - 1) == |
2129 | 0) { |
2130 | p += sizeof("LOG_RNB_REMOTE" ) - 1; |
2131 | bitmask |= LOG_RNB_REMOTE; |
2132 | } else if (strncmp(s1: p, s2: "LOG_RNB_EVENTS" , n: sizeof("LOG_RNB_EVENTS" ) - 1) == |
2133 | 0) { |
2134 | p += sizeof("LOG_RNB_EVENTS" ) - 1; |
2135 | bitmask |= LOG_RNB_EVENTS; |
2136 | } else if (strncmp(s1: p, s2: "LOG_RNB_PROC" , n: sizeof("LOG_RNB_PROC" ) - 1) == |
2137 | 0) { |
2138 | p += sizeof("LOG_RNB_PROC" ) - 1; |
2139 | bitmask |= LOG_RNB_PROC; |
2140 | } else if (strncmp(s1: p, s2: "LOG_RNB_PACKETS" , |
2141 | n: sizeof("LOG_RNB_PACKETS" ) - 1) == 0) { |
2142 | p += sizeof("LOG_RNB_PACKETS" ) - 1; |
2143 | bitmask |= LOG_RNB_PACKETS; |
2144 | } else if (strncmp(s1: p, s2: "LOG_RNB_ALL" , n: sizeof("LOG_RNB_ALL" ) - 1) == 0) { |
2145 | p += sizeof("LOG_RNB_ALL" ) - 1; |
2146 | bitmask |= LOG_RNB_ALL; |
2147 | } else if (strncmp(s1: p, s2: "LOG_RNB_DEFAULT" , |
2148 | n: sizeof("LOG_RNB_DEFAULT" ) - 1) == 0) { |
2149 | p += sizeof("LOG_RNB_DEFAULT" ) - 1; |
2150 | bitmask |= LOG_RNB_DEFAULT; |
2151 | } else if (strncmp(s1: p, s2: "LOG_DARWIN_LOG" , n: sizeof("LOG_DARWIN_LOG" ) - 1) == |
2152 | 0) { |
2153 | p += sizeof("LOG_DARWIN_LOG" ) - 1; |
2154 | bitmask |= LOG_DARWIN_LOG; |
2155 | } else if (strncmp(s1: p, s2: "LOG_RNB_NONE" , n: sizeof("LOG_RNB_NONE" ) - 1) == |
2156 | 0) { |
2157 | p += sizeof("LOG_RNB_NONE" ) - 1; |
2158 | bitmask = 0; |
2159 | } else { |
2160 | /* Unrecognized logging bit; ignore it. */ |
2161 | const char *c = strchr(s: p, c: '|'); |
2162 | if (c) { |
2163 | p = c; |
2164 | } else { |
2165 | c = strchr(s: p, c: ';'); |
2166 | if (c) { |
2167 | p = c; |
2168 | } else { |
2169 | // Improperly terminated word; just go to end of str |
2170 | p = strchr(s: p, c: '\0'); |
2171 | } |
2172 | } |
2173 | } |
2174 | } |
2175 | // Did we get a properly formatted logging bitmask? |
2176 | if (p && *p == ';') { |
2177 | // Enable DNB logging. |
2178 | // Use the existing log callback if one was already configured. |
2179 | if (!DNBLogGetLogCallback()) { |
2180 | // Use the os_log()-based logger if available; otherwise, |
2181 | // fallback to ASL. |
2182 | auto log_callback = OsLogger::GetLogFunction(); |
2183 | if (log_callback) |
2184 | DNBLogSetLogCallback(log_callback, nullptr); |
2185 | else |
2186 | DNBLogSetLogCallback(callback: ASLLogCallback, baton: nullptr); |
2187 | } |
2188 | |
2189 | // Update logging to use the configured log channel bitmask. |
2190 | DNBLogSetLogMask(mask: bitmask); |
2191 | p++; |
2192 | } |
2193 | } |
2194 | // We're not going to support logging to a file for now. All logging |
2195 | // goes through ASL or the previously arranged log callback. |
2196 | #if 0 |
2197 | else if (strncmp (p, "mode=" , sizeof ("mode=" ) - 1) == 0) |
2198 | { |
2199 | p += sizeof ("mode=" ) - 1; |
2200 | if (strncmp (p, "asl;" , sizeof ("asl;" ) - 1) == 0) |
2201 | { |
2202 | DNBLogToASL (); |
2203 | p += sizeof ("asl;" ) - 1; |
2204 | } |
2205 | else if (strncmp (p, "file;" , sizeof ("file;" ) - 1) == 0) |
2206 | { |
2207 | DNBLogToFile (); |
2208 | p += sizeof ("file;" ) - 1; |
2209 | } |
2210 | else |
2211 | { |
2212 | // Ignore unknown argument |
2213 | const char *c = strchr (p, ';'); |
2214 | if (c) |
2215 | p = c + 1; |
2216 | else |
2217 | p = strchr (p, '\0'); |
2218 | } |
2219 | } |
2220 | else if (strncmp (p, "filename=" , sizeof ("filename=" ) - 1) == 0) |
2221 | { |
2222 | p += sizeof ("filename=" ) - 1; |
2223 | const char *c = strchr (p, ';'); |
2224 | if (c == NULL) |
2225 | { |
2226 | c = strchr (p, '\0'); |
2227 | continue; |
2228 | } |
2229 | char *fn = (char *) alloca (c - p + 1); |
2230 | strlcpy (fn, p, c - p); |
2231 | fn[c - p] = '\0'; |
2232 | |
2233 | // A file name of "asl" is special and is another way to indicate |
2234 | // that logging should be done via ASL, not by file. |
2235 | if (strcmp (fn, "asl" ) == 0) |
2236 | { |
2237 | DNBLogToASL (); |
2238 | } |
2239 | else |
2240 | { |
2241 | FILE *f = fopen (fn, "w" ); |
2242 | if (f) |
2243 | { |
2244 | DNBLogSetLogFile (f); |
2245 | DNBEnableLogging (f, DNBLogGetLogMask ()); |
2246 | DNBLogToFile (); |
2247 | } |
2248 | } |
2249 | p = c + 1; |
2250 | } |
2251 | #endif /* #if 0 to enforce ASL logging only. */ |
2252 | else { |
2253 | // Ignore unknown argument |
2254 | const char *c = strchr(s: p, c: ';'); |
2255 | if (c) |
2256 | p = c + 1; |
2257 | else |
2258 | p = strchr(s: p, c: '\0'); |
2259 | } |
2260 | } |
2261 | |
2262 | return rnb_success; |
2263 | } |
2264 | |
2265 | rnb_err_t RNBRemote::HandlePacket_QSetIgnoredExceptions(const char *p) { |
2266 | // We can't set the ignored exceptions if we have a running process: |
2267 | if (m_ctx.HasValidProcessID()) |
2268 | return SendErrorPacket(errcode: "E35" ); |
2269 | |
2270 | p += sizeof("QSetIgnoredExceptions:" ) - 1; |
2271 | bool success = true; |
2272 | while(1) { |
2273 | const char *bar = strchr(s: p, c: '|'); |
2274 | if (bar == nullptr) { |
2275 | success = m_ctx.AddIgnoredException(p); |
2276 | break; |
2277 | } else { |
2278 | std::string exc_str(p, bar - p); |
2279 | if (exc_str.empty()) { |
2280 | success = false; |
2281 | break; |
2282 | } |
2283 | |
2284 | success = m_ctx.AddIgnoredException(exc_str.c_str()); |
2285 | if (!success) |
2286 | break; |
2287 | p = bar + 1; |
2288 | } |
2289 | } |
2290 | if (success) |
2291 | return SendPacket(s: "OK" ); |
2292 | else |
2293 | return SendErrorPacket(errcode: "E36" ); |
2294 | } |
2295 | |
2296 | rnb_err_t RNBRemote::HandlePacket_QThreadSuffixSupported(const char *p) { |
2297 | m_thread_suffix_supported = true; |
2298 | return SendPacket(s: "OK" ); |
2299 | } |
2300 | |
2301 | rnb_err_t RNBRemote::HandlePacket_QStartNoAckMode(const char *p) { |
2302 | // Send the OK packet first so the correct checksum is appended... |
2303 | rnb_err_t result = SendPacket(s: "OK" ); |
2304 | m_noack_mode = true; |
2305 | return result; |
2306 | } |
2307 | |
2308 | rnb_err_t RNBRemote::HandlePacket_QSetLogging(const char *p) { |
2309 | p += sizeof("QSetLogging:" ) - 1; |
2310 | rnb_err_t result = set_logging(p); |
2311 | if (result == rnb_success) |
2312 | return SendPacket(s: "OK" ); |
2313 | else |
2314 | return SendErrorPacket(errcode: "E35" ); |
2315 | } |
2316 | |
2317 | rnb_err_t RNBRemote::HandlePacket_QSetDisableASLR(const char *p) { |
2318 | extern int g_disable_aslr; |
2319 | p += sizeof("QSetDisableASLR:" ) - 1; |
2320 | switch (*p) { |
2321 | case '0': |
2322 | g_disable_aslr = 0; |
2323 | break; |
2324 | case '1': |
2325 | g_disable_aslr = 1; |
2326 | break; |
2327 | default: |
2328 | return SendErrorPacket(errcode: "E56" ); |
2329 | } |
2330 | return SendPacket(s: "OK" ); |
2331 | } |
2332 | |
2333 | rnb_err_t RNBRemote::HandlePacket_QSetSTDIO(const char *p) { |
2334 | // Only set stdin/out/err if we don't already have a process |
2335 | if (!m_ctx.HasValidProcessID()) { |
2336 | bool success = false; |
2337 | // Check the seventh character since the packet will be one of: |
2338 | // QSetSTDIN |
2339 | // QSetSTDOUT |
2340 | // QSetSTDERR |
2341 | StdStringExtractor packet(p); |
2342 | packet.SetFilePos(7); |
2343 | char ch = packet.GetChar(); |
2344 | while (packet.GetChar() != ':') |
2345 | /* Do nothing. */; |
2346 | |
2347 | switch (ch) { |
2348 | case 'I': // STDIN |
2349 | packet.GetHexByteString(m_ctx.GetSTDIN()); |
2350 | success = !m_ctx.GetSTDIN().empty(); |
2351 | break; |
2352 | |
2353 | case 'O': // STDOUT |
2354 | packet.GetHexByteString(m_ctx.GetSTDOUT()); |
2355 | success = !m_ctx.GetSTDOUT().empty(); |
2356 | break; |
2357 | |
2358 | case 'E': // STDERR |
2359 | packet.GetHexByteString(m_ctx.GetSTDERR()); |
2360 | success = !m_ctx.GetSTDERR().empty(); |
2361 | break; |
2362 | |
2363 | default: |
2364 | break; |
2365 | } |
2366 | if (success) |
2367 | return SendPacket(s: "OK" ); |
2368 | return SendErrorPacket(errcode: "E57" ); |
2369 | } |
2370 | return SendErrorPacket(errcode: "E58" ); |
2371 | } |
2372 | |
2373 | rnb_err_t RNBRemote::HandlePacket_QSetWorkingDir(const char *p) { |
2374 | // Only set the working directory if we don't already have a process |
2375 | if (!m_ctx.HasValidProcessID()) { |
2376 | StdStringExtractor packet(p += sizeof("QSetWorkingDir:" ) - 1); |
2377 | if (packet.GetHexByteString(m_ctx.GetWorkingDir())) { |
2378 | struct stat working_dir_stat; |
2379 | if (::stat(m_ctx.GetWorkingDirPath(), &working_dir_stat) == -1) { |
2380 | m_ctx.GetWorkingDir().clear(); |
2381 | return SendErrorPacket(errcode: "E61" ); // Working directory doesn't exist... |
2382 | } else if ((working_dir_stat.st_mode & S_IFMT) == S_IFDIR) { |
2383 | return SendPacket(s: "OK" ); |
2384 | } else { |
2385 | m_ctx.GetWorkingDir().clear(); |
2386 | return SendErrorPacket(errcode: "E62" ); // Working directory isn't a directory... |
2387 | } |
2388 | } |
2389 | return SendErrorPacket(errcode: "E59" ); // Invalid path |
2390 | } |
2391 | return SendPacket( |
2392 | s: "E60" ); // Already had a process, too late to set working dir |
2393 | } |
2394 | |
2395 | rnb_err_t RNBRemote::HandlePacket_QSyncThreadState(const char *p) { |
2396 | if (!m_ctx.HasValidProcessID()) { |
2397 | // We allow gdb to connect to a server that hasn't started running |
2398 | // the target yet. gdb still wants to ask questions about it and |
2399 | // freaks out if it gets an error. So just return OK here. |
2400 | return SendPacket(s: "OK" ); |
2401 | } |
2402 | |
2403 | errno = 0; |
2404 | p += strlen(s: "QSyncThreadState:" ); |
2405 | nub_thread_t tid = strtoul(nptr: p, NULL, base: 16); |
2406 | if (errno != 0 && tid == 0) { |
2407 | return HandlePacket_ILLFORMED( |
2408 | __FILE__, __LINE__, p, |
2409 | description: "Invalid thread number in QSyncThreadState packet" ); |
2410 | } |
2411 | if (DNBProcessSyncThreadState(m_ctx.ProcessID(), tid)) |
2412 | return SendPacket(s: "OK" ); |
2413 | else |
2414 | return SendErrorPacket(errcode: "E61" ); |
2415 | } |
2416 | |
2417 | rnb_err_t RNBRemote::HandlePacket_QSetDetachOnError(const char *p) { |
2418 | p += sizeof("QSetDetachOnError:" ) - 1; |
2419 | bool should_detach = true; |
2420 | switch (*p) { |
2421 | case '0': |
2422 | should_detach = false; |
2423 | break; |
2424 | case '1': |
2425 | should_detach = true; |
2426 | break; |
2427 | default: |
2428 | return HandlePacket_ILLFORMED( |
2429 | __FILE__, __LINE__, p, |
2430 | description: "Invalid value for QSetDetachOnError - should be 0 or 1" ); |
2431 | break; |
2432 | } |
2433 | |
2434 | m_ctx.SetDetachOnError(should_detach); |
2435 | return SendPacket(s: "OK" ); |
2436 | } |
2437 | |
2438 | rnb_err_t RNBRemote::HandlePacket_QListThreadsInStopReply(const char *p) { |
2439 | // If this packet is received, it allows us to send an extra key/value |
2440 | // pair in the stop reply packets where we will list all of the thread IDs |
2441 | // separated by commas: |
2442 | // |
2443 | // "threads:10a,10b,10c;" |
2444 | // |
2445 | // This will get included in the stop reply packet as something like: |
2446 | // |
2447 | // "T11thread:10a;00:00000000;01:00010203:threads:10a,10b,10c;" |
2448 | // |
2449 | // This can save two packets on each stop: qfThreadInfo/qsThreadInfo and |
2450 | // speed things up a bit. |
2451 | // |
2452 | // Send the OK packet first so the correct checksum is appended... |
2453 | rnb_err_t result = SendPacket(s: "OK" ); |
2454 | m_list_threads_in_stop_reply = true; |
2455 | |
2456 | return result; |
2457 | } |
2458 | |
2459 | rnb_err_t RNBRemote::HandlePacket_QSetMaxPayloadSize(const char *p) { |
2460 | /* The number of characters in a packet payload that gdb is |
2461 | prepared to accept. The packet-start char, packet-end char, |
2462 | 2 checksum chars and terminating null character are not included |
2463 | in this size. */ |
2464 | p += sizeof("QSetMaxPayloadSize:" ) - 1; |
2465 | errno = 0; |
2466 | uint32_t size = static_cast<uint32_t>(strtoul(nptr: p, NULL, base: 16)); |
2467 | if (errno != 0 && size == 0) { |
2468 | return HandlePacket_ILLFORMED( |
2469 | __FILE__, __LINE__, p, description: "Invalid length in QSetMaxPayloadSize packet" ); |
2470 | } |
2471 | m_max_payload_size = size; |
2472 | return SendPacket(s: "OK" ); |
2473 | } |
2474 | |
2475 | rnb_err_t RNBRemote::HandlePacket_QSetMaxPacketSize(const char *p) { |
2476 | /* This tells us the largest packet that gdb can handle. |
2477 | i.e. the size of gdb's packet-reading buffer. |
2478 | QSetMaxPayloadSize is preferred because it is less ambiguous. */ |
2479 | p += sizeof("QSetMaxPacketSize:" ) - 1; |
2480 | errno = 0; |
2481 | uint32_t size = static_cast<uint32_t>(strtoul(nptr: p, NULL, base: 16)); |
2482 | if (errno != 0 && size == 0) { |
2483 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
2484 | description: "Invalid length in QSetMaxPacketSize packet" ); |
2485 | } |
2486 | m_max_payload_size = size - 5; |
2487 | return SendPacket(s: "OK" ); |
2488 | } |
2489 | |
2490 | rnb_err_t RNBRemote::HandlePacket_QEnvironment(const char *p) { |
2491 | /* This sets the environment for the target program. The packet is of the |
2492 | form: |
2493 | |
2494 | QEnvironment:VARIABLE=VALUE |
2495 | |
2496 | */ |
2497 | |
2498 | DNBLogThreadedIf( |
2499 | LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironment: \"%s\"" , |
2500 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p); |
2501 | |
2502 | p += sizeof("QEnvironment:" ) - 1; |
2503 | RNBContext &ctx = Context(); |
2504 | |
2505 | ctx.PushEnvironment(arg: p); |
2506 | return SendPacket(s: "OK" ); |
2507 | } |
2508 | |
2509 | rnb_err_t RNBRemote::HandlePacket_QEnvironmentHexEncoded(const char *p) { |
2510 | /* This sets the environment for the target program. The packet is of the |
2511 | form: |
2512 | |
2513 | QEnvironmentHexEncoded:VARIABLE=VALUE |
2514 | |
2515 | The VARIABLE=VALUE part is sent hex-encoded so characters like '#' with |
2516 | special |
2517 | meaning in the remote protocol won't break it. |
2518 | */ |
2519 | |
2520 | DNBLogThreadedIf(LOG_RNB_REMOTE, |
2521 | "%8u RNBRemote::%s Handling QEnvironmentHexEncoded: \"%s\"" , |
2522 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), |
2523 | __FUNCTION__, p); |
2524 | |
2525 | p += sizeof("QEnvironmentHexEncoded:" ) - 1; |
2526 | |
2527 | std::string arg; |
2528 | const char *c; |
2529 | c = p; |
2530 | while (*c != '\0') { |
2531 | if (*(c + 1) == '\0') { |
2532 | return HandlePacket_ILLFORMED( |
2533 | __FILE__, __LINE__, p, |
2534 | description: "non-hex char in arg on 'QEnvironmentHexEncoded' pkt" ); |
2535 | } |
2536 | char smallbuf[3]; |
2537 | smallbuf[0] = *c; |
2538 | smallbuf[1] = *(c + 1); |
2539 | smallbuf[2] = '\0'; |
2540 | errno = 0; |
2541 | int ch = static_cast<int>(strtoul(nptr: smallbuf, NULL, base: 16)); |
2542 | if (errno != 0 && ch == 0) { |
2543 | return HandlePacket_ILLFORMED( |
2544 | __FILE__, __LINE__, p, |
2545 | description: "non-hex char in arg on 'QEnvironmentHexEncoded' pkt" ); |
2546 | } |
2547 | arg.push_back(c: ch); |
2548 | c += 2; |
2549 | } |
2550 | |
2551 | RNBContext &ctx = Context(); |
2552 | if (arg.length() > 0) |
2553 | ctx.PushEnvironment(arg: arg.c_str()); |
2554 | |
2555 | return SendPacket(s: "OK" ); |
2556 | } |
2557 | |
2558 | rnb_err_t RNBRemote::HandlePacket_QLaunchArch(const char *p) { |
2559 | p += sizeof("QLaunchArch:" ) - 1; |
2560 | if (DNBSetArchitecture(arch: p)) |
2561 | return SendPacket(s: "OK" ); |
2562 | return SendErrorPacket(errcode: "E63" ); |
2563 | } |
2564 | |
2565 | rnb_err_t RNBRemote::HandlePacket_QSetProcessEvent(const char *p) { |
2566 | p += sizeof("QSetProcessEvent:" ) - 1; |
2567 | // If the process is running, then send the event to the process, otherwise |
2568 | // store it in the context. |
2569 | if (Context().HasValidProcessID()) { |
2570 | if (DNBProcessSendEvent(pid: Context().ProcessID(), event: p)) |
2571 | return SendPacket(s: "OK" ); |
2572 | else |
2573 | return SendErrorPacket(errcode: "E80" ); |
2574 | } else { |
2575 | Context().PushProcessEvent(p); |
2576 | } |
2577 | return SendPacket(s: "OK" ); |
2578 | } |
2579 | |
2580 | void register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid, |
2581 | nub_thread_t tid, |
2582 | const register_map_entry_t *reg, |
2583 | const DNBRegisterValue *reg_value_ptr) { |
2584 | if (reg != NULL) { |
2585 | DNBRegisterValue reg_value; |
2586 | if (reg_value_ptr == NULL) { |
2587 | if (DNBThreadGetRegisterValueByID(pid, tid, set: reg->nub_info.set, |
2588 | reg: reg->nub_info.reg, value: ®_value)) |
2589 | reg_value_ptr = ®_value; |
2590 | } |
2591 | |
2592 | if (reg_value_ptr) { |
2593 | append_hex_value(ostrm, buf: reg_value_ptr->value.v_uint8, buf_size: reg->nub_info.size, |
2594 | swap: false); |
2595 | } else { |
2596 | // If we fail to read a register value, check if it has a default |
2597 | // fail value. If it does, return this instead in case some of |
2598 | // the registers are not available on the current system. |
2599 | if (reg->nub_info.size > 0) { |
2600 | std::vector<uint8_t> zeros(reg->nub_info.size, '\0'); |
2601 | append_hex_value(ostrm, buf: zeros.data(), buf_size: zeros.size(), swap: false); |
2602 | } |
2603 | } |
2604 | } |
2605 | } |
2606 | |
2607 | void debugserver_regnum_with_fixed_width_hex_register_value( |
2608 | std::ostream &ostrm, nub_process_t pid, nub_thread_t tid, |
2609 | const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr) { |
2610 | // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX |
2611 | // gdb register number, and VVVVVVVV is the correct number of hex bytes |
2612 | // as ASCII for the register value. |
2613 | if (reg != NULL) { |
2614 | ostrm << RAWHEX8(reg->debugserver_regnum) << ':'; |
2615 | register_value_in_hex_fixed_width(ostrm, pid, tid, reg, reg_value_ptr); |
2616 | ostrm << ';'; |
2617 | } |
2618 | } |
2619 | |
2620 | void RNBRemote::DispatchQueueOffsets::GetThreadQueueInfo( |
2621 | nub_process_t pid, nub_addr_t dispatch_qaddr, nub_addr_t &dispatch_queue_t, |
2622 | std::string &queue_name, uint64_t &queue_width, |
2623 | uint64_t &queue_serialnum) const { |
2624 | queue_name.clear(); |
2625 | queue_width = 0; |
2626 | queue_serialnum = 0; |
2627 | |
2628 | if (IsValid() && dispatch_qaddr != INVALID_NUB_ADDRESS && |
2629 | dispatch_qaddr != 0) { |
2630 | dispatch_queue_t = DNBProcessMemoryReadPointer(pid, addr: dispatch_qaddr); |
2631 | if (dispatch_queue_t) { |
2632 | queue_width = DNBProcessMemoryReadInteger( |
2633 | pid, addr: dispatch_queue_t + dqo_width, integer_size: dqo_width_size, fail_value: 0); |
2634 | queue_serialnum = DNBProcessMemoryReadInteger( |
2635 | pid, addr: dispatch_queue_t + dqo_serialnum, integer_size: dqo_serialnum_size, fail_value: 0); |
2636 | |
2637 | if (dqo_version >= 4) { |
2638 | // libdispatch versions 4+, pointer to dispatch name is in the |
2639 | // queue structure. |
2640 | nub_addr_t pointer_to_label_address = dispatch_queue_t + dqo_label; |
2641 | nub_addr_t label_addr = |
2642 | DNBProcessMemoryReadPointer(pid, addr: pointer_to_label_address); |
2643 | if (label_addr) |
2644 | queue_name = DNBProcessMemoryReadCString(pid, addr: label_addr); |
2645 | } else { |
2646 | // libdispatch versions 1-3, dispatch name is a fixed width char array |
2647 | // in the queue structure. |
2648 | queue_name = DNBProcessMemoryReadCStringFixed( |
2649 | pid, addr: dispatch_queue_t + dqo_label, fixed_length: dqo_label_size); |
2650 | } |
2651 | } |
2652 | } |
2653 | } |
2654 | |
2655 | struct StackMemory { |
2656 | uint8_t bytes[2 * sizeof(nub_addr_t)]; |
2657 | nub_size_t length; |
2658 | }; |
2659 | typedef std::map<nub_addr_t, StackMemory> StackMemoryMap; |
2660 | |
2661 | static void ReadStackMemory(nub_process_t pid, nub_thread_t tid, |
2662 | StackMemoryMap &stack_mmap, |
2663 | uint32_t backtrace_limit = 256) { |
2664 | DNBRegisterValue reg_value; |
2665 | if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, |
2666 | GENERIC_REGNUM_FP, value: ®_value)) { |
2667 | uint32_t frame_count = 0; |
2668 | uint64_t fp = 0; |
2669 | if (reg_value.info.size == 4) |
2670 | fp = reg_value.value.uint32; |
2671 | else |
2672 | fp = reg_value.value.uint64; |
2673 | while (fp != 0) { |
2674 | // Make sure we never recurse more than 256 times so we don't recurse too |
2675 | // far or |
2676 | // store up too much memory in the expedited cache |
2677 | if (++frame_count > backtrace_limit) |
2678 | break; |
2679 | |
2680 | const nub_size_t read_size = reg_value.info.size * 2; |
2681 | StackMemory stack_memory; |
2682 | stack_memory.length = read_size; |
2683 | if (DNBProcessMemoryRead(pid, addr: fp, size: read_size, buf: stack_memory.bytes) != |
2684 | read_size) |
2685 | break; |
2686 | // Make sure we don't try to put the same stack memory in more than once |
2687 | if (stack_mmap.find(x: fp) != stack_mmap.end()) |
2688 | break; |
2689 | // Put the entry into the cache |
2690 | stack_mmap[fp] = stack_memory; |
2691 | // Dereference the frame pointer to get to the previous frame pointer |
2692 | if (reg_value.info.size == 4) |
2693 | fp = ((uint32_t *)stack_memory.bytes)[0]; |
2694 | else |
2695 | fp = ((uint64_t *)stack_memory.bytes)[0]; |
2696 | } |
2697 | } |
2698 | } |
2699 | |
2700 | rnb_err_t RNBRemote::SendStopReplyPacketForThread(nub_thread_t tid) { |
2701 | const nub_process_t pid = m_ctx.ProcessID(); |
2702 | if (pid == INVALID_NUB_PROCESS) |
2703 | return SendErrorPacket(errcode: "E50" ); |
2704 | |
2705 | struct DNBThreadStopInfo tid_stop_info; |
2706 | |
2707 | /* Fill the remaining space in this packet with as many registers |
2708 | as we can stuff in there. */ |
2709 | |
2710 | if (DNBThreadGetStopReason(pid, tid, stop_info: &tid_stop_info)) { |
2711 | const bool did_exec = tid_stop_info.reason == eStopTypeExec; |
2712 | if (did_exec) { |
2713 | RNBRemote::InitializeRegisters(force: true); |
2714 | |
2715 | // Reset any symbols that need resetting when we exec |
2716 | m_dispatch_queue_offsets_addr = INVALID_NUB_ADDRESS; |
2717 | m_dispatch_queue_offsets.Clear(); |
2718 | } |
2719 | |
2720 | std::ostringstream ostrm; |
2721 | // Output the T packet with the thread |
2722 | ostrm << 'T'; |
2723 | int signum = tid_stop_info.details.signal.signo; |
2724 | DNBLogThreadedIf( |
2725 | LOG_RNB_PROC, "%8d %s got signal signo = %u, exc_type = %u" , |
2726 | (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, |
2727 | signum, tid_stop_info.details.exception.type); |
2728 | |
2729 | // Translate any mach exceptions to gdb versions, unless they are |
2730 | // common exceptions like a breakpoint or a soft signal. |
2731 | switch (tid_stop_info.details.exception.type) { |
2732 | default: |
2733 | signum = 0; |
2734 | break; |
2735 | case EXC_BREAKPOINT: |
2736 | signum = SIGTRAP; |
2737 | break; |
2738 | case EXC_BAD_ACCESS: |
2739 | signum = TARGET_EXC_BAD_ACCESS; |
2740 | break; |
2741 | case EXC_BAD_INSTRUCTION: |
2742 | signum = TARGET_EXC_BAD_INSTRUCTION; |
2743 | break; |
2744 | case EXC_ARITHMETIC: |
2745 | signum = TARGET_EXC_ARITHMETIC; |
2746 | break; |
2747 | case EXC_EMULATION: |
2748 | signum = TARGET_EXC_EMULATION; |
2749 | break; |
2750 | case EXC_SOFTWARE: |
2751 | if (tid_stop_info.details.exception.data_count == 2 && |
2752 | tid_stop_info.details.exception.data[0] == EXC_SOFT_SIGNAL) |
2753 | signum = static_cast<int>(tid_stop_info.details.exception.data[1]); |
2754 | else |
2755 | signum = TARGET_EXC_SOFTWARE; |
2756 | break; |
2757 | } |
2758 | |
2759 | ostrm << RAWHEX8(signum & 0xff); |
2760 | |
2761 | ostrm << std::hex << "thread:" << tid << ';'; |
2762 | |
2763 | const char *thread_name = DNBThreadGetName(pid, tid); |
2764 | if (thread_name && thread_name[0]) { |
2765 | size_t thread_name_len = strlen(s: thread_name); |
2766 | |
2767 | if (::strcspn(s: thread_name, reject: "$#+-;:" ) == thread_name_len) |
2768 | ostrm << std::hex << "name:" << thread_name << ';'; |
2769 | else { |
2770 | // the thread name contains special chars, send as hex bytes |
2771 | ostrm << std::hex << "hexname:" ; |
2772 | const uint8_t *u_thread_name = (const uint8_t *)thread_name; |
2773 | for (size_t i = 0; i < thread_name_len; i++) |
2774 | ostrm << RAWHEX8(u_thread_name[i]); |
2775 | ostrm << ';'; |
2776 | } |
2777 | } |
2778 | |
2779 | // If a 'QListThreadsInStopReply' was sent to enable this feature, we |
2780 | // will send all thread IDs back in the "threads" key whose value is |
2781 | // a list of hex thread IDs separated by commas: |
2782 | // "threads:10a,10b,10c;" |
2783 | // This will save the debugger from having to send a pair of qfThreadInfo |
2784 | // and qsThreadInfo packets, but it also might take a lot of room in the |
2785 | // stop reply packet, so it must be enabled only on systems where there |
2786 | // are no limits on packet lengths. |
2787 | if (m_list_threads_in_stop_reply) { |
2788 | const nub_size_t numthreads = DNBProcessGetNumThreads(pid); |
2789 | if (numthreads > 0) { |
2790 | std::vector<uint64_t> pc_values; |
2791 | ostrm << std::hex << "threads:" ; |
2792 | for (nub_size_t i = 0; i < numthreads; ++i) { |
2793 | nub_thread_t th = DNBProcessGetThreadAtIndex(pid, thread_idx: i); |
2794 | if (i > 0) |
2795 | ostrm << ','; |
2796 | ostrm << std::hex << th; |
2797 | DNBRegisterValue pc_regval; |
2798 | if (DNBThreadGetRegisterValueByID(pid, tid: th, REGISTER_SET_GENERIC, |
2799 | GENERIC_REGNUM_PC, value: &pc_regval)) { |
2800 | uint64_t pc = INVALID_NUB_ADDRESS; |
2801 | if (pc_regval.value.uint64 != INVALID_NUB_ADDRESS) { |
2802 | if (pc_regval.info.size == 4) { |
2803 | pc = pc_regval.value.uint32; |
2804 | } else if (pc_regval.info.size == 8) { |
2805 | pc = pc_regval.value.uint64; |
2806 | } |
2807 | if (pc != INVALID_NUB_ADDRESS) { |
2808 | pc_values.push_back(x: pc); |
2809 | } |
2810 | } |
2811 | } |
2812 | } |
2813 | ostrm << ';'; |
2814 | |
2815 | // If we failed to get any of the thread pc values, the size of our |
2816 | // vector will not |
2817 | // be the same as the # of threads. Don't provide any expedited thread |
2818 | // pc values in |
2819 | // that case. This should not happen. |
2820 | if (pc_values.size() == numthreads) { |
2821 | ostrm << std::hex << "thread-pcs:" ; |
2822 | for (nub_size_t i = 0; i < numthreads; ++i) { |
2823 | if (i > 0) |
2824 | ostrm << ','; |
2825 | ostrm << std::hex << pc_values[i]; |
2826 | } |
2827 | ostrm << ';'; |
2828 | } |
2829 | } |
2830 | |
2831 | // Include JSON info that describes the stop reason for any threads |
2832 | // that actually have stop reasons. We use the new "jstopinfo" key |
2833 | // whose values is hex ascii JSON that contains the thread IDs |
2834 | // thread stop info only for threads that have stop reasons. Only send |
2835 | // this if we have more than one thread otherwise this packet has all |
2836 | // the info it needs. |
2837 | if (numthreads > 1) { |
2838 | const bool threads_with_valid_stop_info_only = true; |
2839 | JSONGenerator::ObjectSP threads_info_sp = |
2840 | GetJSONThreadsInfo(threads_with_valid_stop_info_only); |
2841 | if (threads_info_sp) { |
2842 | ostrm << std::hex << "jstopinfo:" ; |
2843 | std::ostringstream json_strm; |
2844 | threads_info_sp->Dump(json_strm); |
2845 | threads_info_sp->Clear(); |
2846 | append_hexified_string(ostrm, string: json_strm.str()); |
2847 | ostrm << ';'; |
2848 | } |
2849 | } |
2850 | } |
2851 | |
2852 | if (g_num_reg_entries == 0) |
2853 | InitializeRegisters(); |
2854 | |
2855 | if (g_reg_entries != NULL) { |
2856 | auto interesting_regset = [](int regset) -> bool { |
2857 | #if defined(__arm64__) || defined(__aarch64__) |
2858 | // GPRs and exception registers, helpful for debugging |
2859 | // from packet logs. |
2860 | return regset == 1 || regset == 3; |
2861 | #else |
2862 | return regset == 1; |
2863 | #endif |
2864 | }; |
2865 | |
2866 | DNBRegisterValue reg_value; |
2867 | for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) { |
2868 | // Expedite all registers in the first register set that aren't |
2869 | // contained in other registers |
2870 | if (interesting_regset(g_reg_entries[reg].nub_info.set) && |
2871 | g_reg_entries[reg].nub_info.value_regs == NULL) { |
2872 | if (!DNBThreadGetRegisterValueByID( |
2873 | pid, tid, set: g_reg_entries[reg].nub_info.set, |
2874 | reg: g_reg_entries[reg].nub_info.reg, value: ®_value)) |
2875 | continue; |
2876 | |
2877 | debugserver_regnum_with_fixed_width_hex_register_value( |
2878 | ostrm, pid, tid, reg: &g_reg_entries[reg], reg_value_ptr: ®_value); |
2879 | } |
2880 | } |
2881 | } |
2882 | |
2883 | if (did_exec) { |
2884 | ostrm << "reason:exec;" ; |
2885 | } else if (tid_stop_info.reason == eStopTypeWatchpoint) { |
2886 | ostrm << "reason:watchpoint;" ; |
2887 | ostrm << "description:" ; |
2888 | std::ostringstream wp_desc; |
2889 | wp_desc << tid_stop_info.details.watchpoint.addr << " " ; |
2890 | wp_desc << tid_stop_info.details.watchpoint.hw_idx << " " ; |
2891 | wp_desc << tid_stop_info.details.watchpoint.mach_exception_addr; |
2892 | append_hexified_string(ostrm, string: wp_desc.str()); |
2893 | ostrm << ";" ; |
2894 | |
2895 | // Temporarily, print all of the fields we've parsed out of the ESR |
2896 | // on a watchpoint exception. Normally this is something we would |
2897 | // log for LOG_WATCHPOINTS only, but this was implemented from the |
2898 | // ARM ARM spec and hasn't been exercised on real hardware that can |
2899 | // set most of these fields yet. It may need to be debugged in the |
2900 | // future, so include all of these purely for debugging by reading |
2901 | // the packet logs; lldb isn't using these fields. |
2902 | ostrm << "watch_addr:" << std::hex |
2903 | << tid_stop_info.details.watchpoint.addr << ";" ; |
2904 | ostrm << "me_watch_addr:" << std::hex |
2905 | << tid_stop_info.details.watchpoint.mach_exception_addr << ";" ; |
2906 | ostrm << "wp_hw_idx:" << std::hex |
2907 | << tid_stop_info.details.watchpoint.hw_idx << ";" ; |
2908 | if (tid_stop_info.details.watchpoint.esr_fields_set) { |
2909 | ostrm << "wp_esr_iss:" << std::hex |
2910 | << tid_stop_info.details.watchpoint.esr_fields.iss << ";" ; |
2911 | ostrm << "wp_esr_wpt:" << std::hex |
2912 | << tid_stop_info.details.watchpoint.esr_fields.wpt << ";" ; |
2913 | ostrm << "wp_esr_wptv:" |
2914 | << tid_stop_info.details.watchpoint.esr_fields.wptv << ";" ; |
2915 | ostrm << "wp_esr_wpf:" |
2916 | << tid_stop_info.details.watchpoint.esr_fields.wpf << ";" ; |
2917 | ostrm << "wp_esr_fnp:" |
2918 | << tid_stop_info.details.watchpoint.esr_fields.fnp << ";" ; |
2919 | ostrm << "wp_esr_vncr:" |
2920 | << tid_stop_info.details.watchpoint.esr_fields.vncr << ";" ; |
2921 | ostrm << "wp_esr_fnv:" |
2922 | << tid_stop_info.details.watchpoint.esr_fields.fnv << ";" ; |
2923 | ostrm << "wp_esr_cm:" << tid_stop_info.details.watchpoint.esr_fields.cm |
2924 | << ";" ; |
2925 | ostrm << "wp_esr_wnr:" |
2926 | << tid_stop_info.details.watchpoint.esr_fields.wnr << ";" ; |
2927 | ostrm << "wp_esr_dfsc:" << std::hex |
2928 | << tid_stop_info.details.watchpoint.esr_fields.dfsc << ";" ; |
2929 | } |
2930 | } else if (tid_stop_info.details.exception.type) { |
2931 | ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type |
2932 | << ';'; |
2933 | ostrm << "mecount:" << std::hex |
2934 | << tid_stop_info.details.exception.data_count << ';'; |
2935 | for (nub_size_t i = 0; i < tid_stop_info.details.exception.data_count; |
2936 | ++i) |
2937 | ostrm << "medata:" << std::hex |
2938 | << tid_stop_info.details.exception.data[i] << ';'; |
2939 | } |
2940 | |
2941 | // Add expedited stack memory so stack backtracing doesn't need to read |
2942 | // anything from the |
2943 | // frame pointer chain. |
2944 | StackMemoryMap stack_mmap; |
2945 | ReadStackMemory(pid, tid, stack_mmap, backtrace_limit: 2); |
2946 | if (!stack_mmap.empty()) { |
2947 | for (const auto &stack_memory : stack_mmap) { |
2948 | ostrm << "memory:" << HEXBASE << stack_memory.first << '='; |
2949 | append_hex_value(ostrm, buf: stack_memory.second.bytes, |
2950 | buf_size: stack_memory.second.length, swap: false); |
2951 | ostrm << ';'; |
2952 | } |
2953 | } |
2954 | |
2955 | return SendPacket(s: ostrm.str()); |
2956 | } |
2957 | return SendErrorPacket(errcode: "E51" ); |
2958 | } |
2959 | |
2960 | /* '?' |
2961 | The stop reply packet - tell gdb what the status of the inferior is. |
2962 | Often called the questionmark_packet. */ |
2963 | |
2964 | rnb_err_t RNBRemote::HandlePacket_last_signal(const char *unused) { |
2965 | if (!m_ctx.HasValidProcessID()) { |
2966 | // Inferior is not yet specified/running |
2967 | return SendErrorPacket(errcode: "E02" ); |
2968 | } |
2969 | |
2970 | nub_process_t pid = m_ctx.ProcessID(); |
2971 | nub_state_t pid_state = DNBProcessGetState(pid); |
2972 | |
2973 | switch (pid_state) { |
2974 | case eStateAttaching: |
2975 | case eStateLaunching: |
2976 | case eStateRunning: |
2977 | case eStateStepping: |
2978 | case eStateDetached: |
2979 | return rnb_success; // Ignore |
2980 | |
2981 | case eStateSuspended: |
2982 | case eStateStopped: |
2983 | case eStateCrashed: { |
2984 | nub_thread_t tid = DNBProcessGetCurrentThread(pid); |
2985 | // Make sure we set the current thread so g and p packets return |
2986 | // the data the gdb will expect. |
2987 | SetCurrentThread(tid); |
2988 | |
2989 | SendStopReplyPacketForThread(tid); |
2990 | } break; |
2991 | |
2992 | case eStateInvalid: |
2993 | case eStateUnloaded: |
2994 | case eStateExited: { |
2995 | char pid_exited_packet[16] = "" ; |
2996 | int pid_status = 0; |
2997 | // Process exited with exit status |
2998 | if (!DNBProcessGetExitStatus(pid, status: &pid_status)) |
2999 | pid_status = 0; |
3000 | |
3001 | if (pid_status) { |
3002 | if (WIFEXITED(pid_status)) |
3003 | snprintf(s: pid_exited_packet, maxlen: sizeof(pid_exited_packet), format: "W%02x" , |
3004 | WEXITSTATUS(pid_status)); |
3005 | else if (WIFSIGNALED(pid_status)) |
3006 | snprintf(s: pid_exited_packet, maxlen: sizeof(pid_exited_packet), format: "X%02x" , |
3007 | WTERMSIG(pid_status)); |
3008 | else if (WIFSTOPPED(pid_status)) |
3009 | snprintf(s: pid_exited_packet, maxlen: sizeof(pid_exited_packet), format: "S%02x" , |
3010 | WSTOPSIG(pid_status)); |
3011 | } |
3012 | |
3013 | // If we have an empty exit packet, lets fill one in to be safe. |
3014 | if (!pid_exited_packet[0]) { |
3015 | strlcpy(pid_exited_packet, "W00" , sizeof(pid_exited_packet) - 1); |
3016 | pid_exited_packet[sizeof(pid_exited_packet) - 1] = '\0'; |
3017 | } |
3018 | |
3019 | const char *exit_info = DNBProcessGetExitInfo(pid); |
3020 | if (exit_info != NULL && *exit_info != '\0') { |
3021 | std::ostringstream exit_packet; |
3022 | exit_packet << pid_exited_packet; |
3023 | exit_packet << ';'; |
3024 | exit_packet << RAW_HEXBASE << "description" ; |
3025 | exit_packet << ':'; |
3026 | for (size_t i = 0; exit_info[i] != '\0'; i++) |
3027 | exit_packet << RAWHEX8(exit_info[i]); |
3028 | exit_packet << ';'; |
3029 | return SendPacket(s: exit_packet.str()); |
3030 | } else |
3031 | return SendPacket(s: pid_exited_packet); |
3032 | } break; |
3033 | } |
3034 | return rnb_success; |
3035 | } |
3036 | |
3037 | rnb_err_t RNBRemote::HandlePacket_M(const char *p) { |
3038 | if (p == NULL || p[0] == '\0' || strlen(s: p) < 3) { |
3039 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, description: "Too short M packet" ); |
3040 | } |
3041 | |
3042 | char *c; |
3043 | p++; |
3044 | errno = 0; |
3045 | nub_addr_t addr = strtoull(nptr: p, endptr: &c, base: 16); |
3046 | if (errno != 0 && addr == 0) { |
3047 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3048 | description: "Invalid address in M packet" ); |
3049 | } |
3050 | if (*c != ',') { |
3051 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3052 | description: "Comma sep missing in M packet" ); |
3053 | } |
3054 | |
3055 | /* Advance 'p' to the length part of the packet. */ |
3056 | p += (c - p) + 1; |
3057 | |
3058 | errno = 0; |
3059 | unsigned long length = strtoul(nptr: p, endptr: &c, base: 16); |
3060 | if (errno != 0 && length == 0) { |
3061 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3062 | description: "Invalid length in M packet" ); |
3063 | } |
3064 | if (length == 0) { |
3065 | return SendPacket(s: "OK" ); |
3066 | } |
3067 | |
3068 | if (*c != ':') { |
3069 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3070 | description: "Missing colon in M packet" ); |
3071 | } |
3072 | /* Advance 'p' to the data part of the packet. */ |
3073 | p += (c - p) + 1; |
3074 | |
3075 | size_t datalen = strlen(s: p); |
3076 | if (datalen & 0x1) { |
3077 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3078 | description: "Uneven # of hex chars for data in M packet" ); |
3079 | } |
3080 | if (datalen == 0) { |
3081 | return SendPacket(s: "OK" ); |
3082 | } |
3083 | |
3084 | uint8_t *buf = (uint8_t *)alloca(datalen / 2); |
3085 | uint8_t *i = buf; |
3086 | |
3087 | while (*p != '\0' && *(p + 1) != '\0') { |
3088 | char hexbuf[3]; |
3089 | hexbuf[0] = *p; |
3090 | hexbuf[1] = *(p + 1); |
3091 | hexbuf[2] = '\0'; |
3092 | errno = 0; |
3093 | uint8_t byte = strtoul(nptr: hexbuf, NULL, base: 16); |
3094 | if (errno != 0 && byte == 0) { |
3095 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3096 | description: "Invalid hex byte in M packet" ); |
3097 | } |
3098 | *i++ = byte; |
3099 | p += 2; |
3100 | } |
3101 | |
3102 | nub_size_t wrote = |
3103 | DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, length, buf); |
3104 | if (wrote != length) |
3105 | return SendErrorPacket(errcode: "E09" ); |
3106 | else |
3107 | return SendPacket(s: "OK" ); |
3108 | } |
3109 | |
3110 | rnb_err_t RNBRemote::HandlePacket_m(const char *p) { |
3111 | if (p == NULL || p[0] == '\0' || strlen(s: p) < 3) { |
3112 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, description: "Too short m packet" ); |
3113 | } |
3114 | |
3115 | char *c; |
3116 | p++; |
3117 | errno = 0; |
3118 | nub_addr_t addr = strtoull(nptr: p, endptr: &c, base: 16); |
3119 | if (errno != 0 && addr == 0) { |
3120 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3121 | description: "Invalid address in m packet" ); |
3122 | } |
3123 | if (*c != ',') { |
3124 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3125 | description: "Comma sep missing in m packet" ); |
3126 | } |
3127 | |
3128 | /* Advance 'p' to the length part of the packet. */ |
3129 | p += (c - p) + 1; |
3130 | |
3131 | errno = 0; |
3132 | auto length = strtoul(nptr: p, NULL, base: 16); |
3133 | if (errno != 0 && length == 0) { |
3134 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3135 | description: "Invalid length in m packet" ); |
3136 | } |
3137 | if (length == 0) { |
3138 | return SendPacket(s: "" ); |
3139 | } |
3140 | |
3141 | std::string buf(length, '\0'); |
3142 | if (buf.empty()) { |
3143 | return SendErrorPacket(errcode: "E78" ); |
3144 | } |
3145 | nub_size_t bytes_read = |
3146 | DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]); |
3147 | if (bytes_read == 0) { |
3148 | return SendErrorPacket(errcode: "E08" ); |
3149 | } |
3150 | |
3151 | // "The reply may contain fewer bytes than requested if the server was able |
3152 | // to read only part of the region of memory." |
3153 | length = bytes_read; |
3154 | |
3155 | std::ostringstream ostrm; |
3156 | for (unsigned long i = 0; i < length; i++) |
3157 | ostrm << RAWHEX8(buf[i]); |
3158 | return SendPacket(s: ostrm.str()); |
3159 | } |
3160 | |
3161 | // Read memory, sent it up as binary data. |
3162 | // Usage: xADDR,LEN |
3163 | // ADDR and LEN are both base 16. |
3164 | |
3165 | // Responds with 'OK' for zero-length request |
3166 | // or |
3167 | // |
3168 | // DATA |
3169 | // |
3170 | // where DATA is the binary data payload. |
3171 | |
3172 | rnb_err_t RNBRemote::HandlePacket_x(const char *p) { |
3173 | if (p == NULL || p[0] == '\0' || strlen(s: p) < 3) { |
3174 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, description: "Too short X packet" ); |
3175 | } |
3176 | |
3177 | char *c; |
3178 | p++; |
3179 | errno = 0; |
3180 | nub_addr_t addr = strtoull(nptr: p, endptr: &c, base: 16); |
3181 | if (errno != 0) { |
3182 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3183 | description: "Invalid address in X packet" ); |
3184 | } |
3185 | if (*c != ',') { |
3186 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3187 | description: "Comma sep missing in X packet" ); |
3188 | } |
3189 | |
3190 | /* Advance 'p' to the number of bytes to be read. */ |
3191 | p += (c - p) + 1; |
3192 | |
3193 | errno = 0; |
3194 | auto length = strtoul(nptr: p, NULL, base: 16); |
3195 | if (errno != 0) { |
3196 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3197 | description: "Invalid length in x packet" ); |
3198 | } |
3199 | |
3200 | // zero length read means this is a test of whether that packet is implemented |
3201 | // or not. |
3202 | if (length == 0) { |
3203 | return SendPacket(s: "OK" ); |
3204 | } |
3205 | |
3206 | std::vector<uint8_t> buf(length); |
3207 | |
3208 | if (buf.capacity() != length) { |
3209 | return SendErrorPacket(errcode: "E79" ); |
3210 | } |
3211 | nub_size_t bytes_read = |
3212 | DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]); |
3213 | if (bytes_read == 0) { |
3214 | return SendErrorPacket(errcode: "E80" ); |
3215 | } |
3216 | |
3217 | std::vector<uint8_t> buf_quoted; |
3218 | buf_quoted.reserve(n: bytes_read + 30); |
3219 | for (nub_size_t i = 0; i < bytes_read; i++) { |
3220 | if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') { |
3221 | buf_quoted.push_back(x: 0x7d); |
3222 | buf_quoted.push_back(x: buf[i] ^ 0x20); |
3223 | } else { |
3224 | buf_quoted.push_back(x: buf[i]); |
3225 | } |
3226 | } |
3227 | length = buf_quoted.size(); |
3228 | |
3229 | std::ostringstream ostrm; |
3230 | for (unsigned long i = 0; i < length; i++) |
3231 | ostrm << buf_quoted[i]; |
3232 | |
3233 | return SendPacket(s: ostrm.str()); |
3234 | } |
3235 | |
3236 | rnb_err_t RNBRemote::HandlePacket_X(const char *p) { |
3237 | if (p == NULL || p[0] == '\0' || strlen(s: p) < 3) { |
3238 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, description: "Too short X packet" ); |
3239 | } |
3240 | |
3241 | char *c; |
3242 | p++; |
3243 | errno = 0; |
3244 | nub_addr_t addr = strtoull(nptr: p, endptr: &c, base: 16); |
3245 | if (errno != 0 && addr == 0) { |
3246 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3247 | description: "Invalid address in X packet" ); |
3248 | } |
3249 | if (*c != ',') { |
3250 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3251 | description: "Comma sep missing in X packet" ); |
3252 | } |
3253 | |
3254 | /* Advance 'p' to the length part of the packet. NB this is the length of the |
3255 | packet |
3256 | including any escaped chars. The data payload may be a little bit smaller |
3257 | after |
3258 | decoding. */ |
3259 | p += (c - p) + 1; |
3260 | |
3261 | errno = 0; |
3262 | auto length = strtoul(nptr: p, NULL, base: 16); |
3263 | if (errno != 0 && length == 0) { |
3264 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3265 | description: "Invalid length in X packet" ); |
3266 | } |
3267 | |
3268 | // I think gdb sends a zero length write request to test whether this |
3269 | // packet is accepted. |
3270 | if (length == 0) { |
3271 | return SendPacket(s: "OK" ); |
3272 | } |
3273 | |
3274 | std::vector<uint8_t> data = decode_binary_data(str: c, len: -1); |
3275 | std::vector<uint8_t>::const_iterator it; |
3276 | uint8_t *buf = (uint8_t *)alloca(data.size()); |
3277 | uint8_t *i = buf; |
3278 | for (it = data.begin(); it != data.end(); ++it) { |
3279 | *i++ = *it; |
3280 | } |
3281 | |
3282 | nub_size_t wrote = |
3283 | DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, data.size(), buf); |
3284 | if (wrote != data.size()) |
3285 | return SendErrorPacket(errcode: "E08" ); |
3286 | return SendPacket(s: "OK" ); |
3287 | } |
3288 | |
3289 | /* 'g' -- read registers |
3290 | Get the contents of the registers for the current thread, |
3291 | send them to gdb. |
3292 | Should the setting of the Hg packet determine which thread's registers |
3293 | are returned? */ |
3294 | |
3295 | rnb_err_t RNBRemote::HandlePacket_g(const char *p) { |
3296 | std::ostringstream ostrm; |
3297 | if (!m_ctx.HasValidProcessID()) { |
3298 | return SendErrorPacket(errcode: "E11" ); |
3299 | } |
3300 | |
3301 | if (g_num_reg_entries == 0) |
3302 | InitializeRegisters(); |
3303 | |
3304 | nub_process_t pid = m_ctx.ProcessID(); |
3305 | nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p: p + 1); |
3306 | if (tid == INVALID_NUB_THREAD) |
3307 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3308 | description: "No thread specified in p packet" ); |
3309 | |
3310 | // Get the register context size first by calling with NULL buffer |
3311 | nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, buf_len: 0); |
3312 | if (reg_ctx_size) { |
3313 | // Now allocate enough space for the entire register context |
3314 | std::vector<uint8_t> reg_ctx; |
3315 | reg_ctx.resize(new_size: reg_ctx_size); |
3316 | // Now read the register context |
3317 | reg_ctx_size = |
3318 | DNBThreadGetRegisterContext(pid, tid, buf: ®_ctx[0], buf_len: reg_ctx.size()); |
3319 | if (reg_ctx_size) { |
3320 | append_hex_value(ostrm, buf: reg_ctx.data(), buf_size: reg_ctx.size(), swap: false); |
3321 | return SendPacket(s: ostrm.str()); |
3322 | } |
3323 | } |
3324 | return SendErrorPacket(errcode: "E74" ); |
3325 | } |
3326 | |
3327 | /* 'G XXX...' -- write registers |
3328 | How is the thread for these specified, beyond "the current thread"? |
3329 | Does gdb actually use the Hg packet to set this? */ |
3330 | |
3331 | rnb_err_t RNBRemote::HandlePacket_G(const char *p) { |
3332 | if (!m_ctx.HasValidProcessID()) { |
3333 | return SendErrorPacket(errcode: "E11" ); |
3334 | } |
3335 | |
3336 | if (g_num_reg_entries == 0) |
3337 | InitializeRegisters(); |
3338 | |
3339 | StdStringExtractor packet(p); |
3340 | packet.SetFilePos(1); // Skip the 'G' |
3341 | |
3342 | nub_process_t pid = m_ctx.ProcessID(); |
3343 | nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); |
3344 | if (tid == INVALID_NUB_THREAD) |
3345 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3346 | description: "No thread specified in p packet" ); |
3347 | |
3348 | // Get the register context size first by calling with NULL buffer |
3349 | nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, buf_len: 0); |
3350 | if (reg_ctx_size) { |
3351 | // Now allocate enough space for the entire register context |
3352 | std::vector<uint8_t> reg_ctx; |
3353 | reg_ctx.resize(new_size: reg_ctx_size); |
3354 | |
3355 | const nub_size_t = |
3356 | packet.GetHexBytes(dst: ®_ctx[0], dst_len: reg_ctx.size(), fail_fill_value: 0xcc); |
3357 | if (bytes_extracted == reg_ctx.size()) { |
3358 | // Now write the register context |
3359 | reg_ctx_size = |
3360 | DNBThreadSetRegisterContext(pid, tid, buf: reg_ctx.data(), buf_len: reg_ctx.size()); |
3361 | if (reg_ctx_size == reg_ctx.size()) |
3362 | return SendPacket(s: "OK" ); |
3363 | else |
3364 | return SendErrorPacket(errcode: "E55" ); |
3365 | } else { |
3366 | DNBLogError("RNBRemote::HandlePacket_G(%s): extracted %llu of %llu " |
3367 | "bytes, size mismatch\n" , |
3368 | p, (uint64_t)bytes_extracted, (uint64_t)reg_ctx_size); |
3369 | return SendErrorPacket(errcode: "E64" ); |
3370 | } |
3371 | } |
3372 | return SendErrorPacket(errcode: "E65" ); |
3373 | } |
3374 | |
3375 | static bool RNBRemoteShouldCancelCallback(void *not_used) { |
3376 | RNBRemoteSP remoteSP(g_remoteSP); |
3377 | if (remoteSP.get() != NULL) { |
3378 | RNBRemote *remote = remoteSP.get(); |
3379 | return !remote->Comm().IsConnected(); |
3380 | } |
3381 | return true; |
3382 | } |
3383 | |
3384 | // FORMAT: _MXXXXXX,PPP |
3385 | // XXXXXX: big endian hex chars |
3386 | // PPP: permissions can be any combo of r w x chars |
3387 | // |
3388 | // RESPONSE: XXXXXX |
3389 | // XXXXXX: hex address of the newly allocated memory |
3390 | // EXX: error code |
3391 | // |
3392 | // EXAMPLES: |
3393 | // _M123000,rw |
3394 | // _M123000,rwx |
3395 | // _M123000,xw |
3396 | |
3397 | rnb_err_t RNBRemote::HandlePacket_AllocateMemory(const char *p) { |
3398 | StdStringExtractor packet(p); |
3399 | packet.SetFilePos(2); // Skip the "_M" |
3400 | |
3401 | nub_addr_t size = packet.GetHexMaxU64(little_endian: StdStringExtractor::BigEndian, fail_value: 0); |
3402 | if (size != 0) { |
3403 | if (packet.GetChar() == ',') { |
3404 | uint32_t permissions = 0; |
3405 | char ch; |
3406 | bool success = true; |
3407 | while (success && (ch = packet.GetChar()) != '\0') { |
3408 | switch (ch) { |
3409 | case 'r': |
3410 | permissions |= eMemoryPermissionsReadable; |
3411 | break; |
3412 | case 'w': |
3413 | permissions |= eMemoryPermissionsWritable; |
3414 | break; |
3415 | case 'x': |
3416 | permissions |= eMemoryPermissionsExecutable; |
3417 | break; |
3418 | default: |
3419 | success = false; |
3420 | break; |
3421 | } |
3422 | } |
3423 | |
3424 | if (success) { |
3425 | nub_addr_t addr = |
3426 | DNBProcessMemoryAllocate(m_ctx.ProcessID(), size, permissions); |
3427 | if (addr != INVALID_NUB_ADDRESS) { |
3428 | std::ostringstream ostrm; |
3429 | ostrm << RAW_HEXBASE << addr; |
3430 | return SendPacket(s: ostrm.str()); |
3431 | } |
3432 | } |
3433 | } |
3434 | } |
3435 | return SendErrorPacket(errcode: "E53" ); |
3436 | } |
3437 | |
3438 | // FORMAT: _mXXXXXX |
3439 | // XXXXXX: address that was previously allocated |
3440 | // |
3441 | // RESPONSE: XXXXXX |
3442 | // OK: address was deallocated |
3443 | // EXX: error code |
3444 | // |
3445 | // EXAMPLES: |
3446 | // _m123000 |
3447 | |
3448 | rnb_err_t RNBRemote::HandlePacket_DeallocateMemory(const char *p) { |
3449 | StdStringExtractor packet(p); |
3450 | packet.SetFilePos(2); // Skip the "_m" |
3451 | nub_addr_t addr = |
3452 | packet.GetHexMaxU64(little_endian: StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS); |
3453 | |
3454 | if (addr != INVALID_NUB_ADDRESS) { |
3455 | if (DNBProcessMemoryDeallocate(m_ctx.ProcessID(), addr)) |
3456 | return SendPacket(s: "OK" ); |
3457 | } |
3458 | return SendErrorPacket(errcode: "E54" ); |
3459 | } |
3460 | |
3461 | // FORMAT: QSaveRegisterState;thread:TTTT; (when thread suffix is supported) |
3462 | // FORMAT: QSaveRegisterState (when thread suffix is NOT |
3463 | // supported) |
3464 | // TTTT: thread ID in hex |
3465 | // |
3466 | // RESPONSE: |
3467 | // SAVEID: Where SAVEID is a decimal number that represents the save ID |
3468 | // that can be passed back into a "QRestoreRegisterState" packet |
3469 | // EXX: error code |
3470 | // |
3471 | // EXAMPLES: |
3472 | // QSaveRegisterState;thread:1E34; (when thread suffix is supported) |
3473 | // QSaveRegisterState (when thread suffix is NOT |
3474 | // supported) |
3475 | |
3476 | rnb_err_t RNBRemote::HandlePacket_SaveRegisterState(const char *p) { |
3477 | nub_process_t pid = m_ctx.ProcessID(); |
3478 | nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); |
3479 | if (tid == INVALID_NUB_THREAD) { |
3480 | if (m_thread_suffix_supported) |
3481 | return HandlePacket_ILLFORMED( |
3482 | __FILE__, __LINE__, p, |
3483 | description: "No thread specified in QSaveRegisterState packet" ); |
3484 | else |
3485 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3486 | description: "No thread was is set with the Hg packet" ); |
3487 | } |
3488 | |
3489 | // Get the register context size first by calling with NULL buffer |
3490 | const uint32_t save_id = DNBThreadSaveRegisterState(pid, tid); |
3491 | if (save_id != 0) { |
3492 | char response[64]; |
3493 | snprintf(s: response, maxlen: sizeof(response), format: "%u" , save_id); |
3494 | return SendPacket(s: response); |
3495 | } else { |
3496 | return SendErrorPacket(errcode: "E75" ); |
3497 | } |
3498 | } |
3499 | // FORMAT: QRestoreRegisterState:SAVEID;thread:TTTT; (when thread suffix is |
3500 | // supported) |
3501 | // FORMAT: QRestoreRegisterState:SAVEID (when thread suffix is NOT |
3502 | // supported) |
3503 | // TTTT: thread ID in hex |
3504 | // SAVEID: a decimal number that represents the save ID that was |
3505 | // returned from a call to "QSaveRegisterState" |
3506 | // |
3507 | // RESPONSE: |
3508 | // OK: successfully restored registers for the specified thread |
3509 | // EXX: error code |
3510 | // |
3511 | // EXAMPLES: |
3512 | // QRestoreRegisterState:1;thread:1E34; (when thread suffix is |
3513 | // supported) |
3514 | // QRestoreRegisterState:1 (when thread suffix is NOT |
3515 | // supported) |
3516 | |
3517 | rnb_err_t RNBRemote::HandlePacket_RestoreRegisterState(const char *p) { |
3518 | nub_process_t pid = m_ctx.ProcessID(); |
3519 | nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); |
3520 | if (tid == INVALID_NUB_THREAD) { |
3521 | if (m_thread_suffix_supported) |
3522 | return HandlePacket_ILLFORMED( |
3523 | __FILE__, __LINE__, p, |
3524 | description: "No thread specified in QSaveRegisterState packet" ); |
3525 | else |
3526 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3527 | description: "No thread was is set with the Hg packet" ); |
3528 | } |
3529 | |
3530 | StdStringExtractor packet(p); |
3531 | packet.SetFilePos( |
3532 | strlen(s: "QRestoreRegisterState:" )); // Skip the "QRestoreRegisterState:" |
3533 | const uint32_t save_id = packet.GetU32(fail_value: 0); |
3534 | |
3535 | if (save_id != 0) { |
3536 | // Get the register context size first by calling with NULL buffer |
3537 | if (DNBThreadRestoreRegisterState(pid, tid, save_id)) |
3538 | return SendPacket(s: "OK" ); |
3539 | else |
3540 | return SendErrorPacket(errcode: "E77" ); |
3541 | } |
3542 | return SendErrorPacket(errcode: "E76" ); |
3543 | } |
3544 | |
3545 | static bool GetProcessNameFrom_vAttach(const char *&p, |
3546 | std::string &attach_name) { |
3547 | bool return_val = true; |
3548 | while (*p != '\0') { |
3549 | char smallbuf[3]; |
3550 | smallbuf[0] = *p; |
3551 | smallbuf[1] = *(p + 1); |
3552 | smallbuf[2] = '\0'; |
3553 | |
3554 | errno = 0; |
3555 | int ch = static_cast<int>(strtoul(nptr: smallbuf, NULL, base: 16)); |
3556 | if (errno != 0 && ch == 0) { |
3557 | return_val = false; |
3558 | break; |
3559 | } |
3560 | |
3561 | attach_name.push_back(c: ch); |
3562 | p += 2; |
3563 | } |
3564 | return return_val; |
3565 | } |
3566 | |
3567 | rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) { |
3568 | uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet |
3569 | // size--debugger can always use less |
3570 | std::stringstream reply; |
3571 | reply << "qXfer:features:read+;PacketSize=" << std::hex << max_packet_size |
3572 | << ";" ; |
3573 | reply << "qEcho+;native-signals+;" ; |
3574 | |
3575 | bool enable_compression = false; |
3576 | (void)enable_compression; |
3577 | |
3578 | #if (defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1) || \ |
3579 | (defined(TARGET_OS_IOS) && TARGET_OS_IOS == 1) || \ |
3580 | (defined(TARGET_OS_TV) && TARGET_OS_TV == 1) || \ |
3581 | (defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1) || \ |
3582 | (defined(TARGET_OS_XR) && TARGET_OS_XR == 1) |
3583 | enable_compression = true; |
3584 | #endif |
3585 | |
3586 | if (enable_compression) { |
3587 | reply << "SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;" ; |
3588 | } |
3589 | |
3590 | #if (defined(__arm64__) || defined(__aarch64__)) |
3591 | reply << "SupportedWatchpointTypes=aarch64-mask,aarch64-bas;" ; |
3592 | #endif |
3593 | #if defined(__x86_64__) |
3594 | reply << "SupportedWatchpointTypes=x86_64;" ; |
3595 | #endif |
3596 | |
3597 | return SendPacket(s: reply.str().c_str()); |
3598 | } |
3599 | |
3600 | static bool process_does_not_exist (nub_process_t pid) { |
3601 | std::vector<struct kinfo_proc> proc_infos; |
3602 | DNBGetAllInfos (proc_infos); |
3603 | const size_t infos_size = proc_infos.size(); |
3604 | for (size_t i = 0; i < infos_size; i++) |
3605 | if (proc_infos[i].kp_proc.p_pid == pid) |
3606 | return false; |
3607 | |
3608 | return true; // process does not exist |
3609 | } |
3610 | |
3611 | // my_uid and process_uid are only initialized if this function |
3612 | // returns true -- that there was a uid mismatch -- and those |
3613 | // id's may want to be used in the error message. |
3614 | // |
3615 | // NOTE: this should only be called after process_does_not_exist(). |
3616 | // This sysctl will return uninitialized data if we ask for a pid |
3617 | // that doesn't exist. The alternative would be to fetch all |
3618 | // processes and step through to find the one we're looking for |
3619 | // (as process_does_not_exist() does). |
3620 | static bool attach_failed_due_to_uid_mismatch (nub_process_t pid, |
3621 | uid_t &my_uid, |
3622 | uid_t &process_uid) { |
3623 | struct kinfo_proc kinfo; |
3624 | int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; |
3625 | size_t len = sizeof(struct kinfo_proc); |
3626 | if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kinfo, &len, NULL, 0) != 0) { |
3627 | return false; // pid doesn't exist? can't check uid mismatch - it was fine |
3628 | } |
3629 | my_uid = geteuid(); |
3630 | if (my_uid == 0) |
3631 | return false; // if we're root, attach didn't fail because of uid mismatch |
3632 | process_uid = kinfo.kp_eproc.e_ucred.cr_uid; |
3633 | |
3634 | // If my uid != the process' uid, then the attach probably failed because |
3635 | // of that. |
3636 | if (my_uid != process_uid) |
3637 | return true; |
3638 | else |
3639 | return false; |
3640 | } |
3641 | |
3642 | // NOTE: this should only be called after process_does_not_exist(). |
3643 | // This sysctl will return uninitialized data if we ask for a pid |
3644 | // that doesn't exist. The alternative would be to fetch all |
3645 | // processes and step through to find the one we're looking for |
3646 | // (as process_does_not_exist() does). |
3647 | static bool process_is_already_being_debugged (nub_process_t pid) { |
3648 | if (DNBProcessIsBeingDebugged(pid) && DNBGetParentProcessID(child_pid: pid) != getpid()) |
3649 | return true; |
3650 | else |
3651 | return false; |
3652 | } |
3653 | |
3654 | // Test if this current login session has a connection to the |
3655 | // window server (if it does not have that access, it cannot ask |
3656 | // for debug permission by popping up a dialog box and attach |
3657 | // may fail outright). |
3658 | static bool login_session_has_gui_access () { |
3659 | // I believe this API only works on macOS. |
3660 | #if TARGET_OS_OSX == 0 |
3661 | return true; |
3662 | #else |
3663 | auditinfo_addr_t info; |
3664 | getaudit_addr(&info, sizeof(info)); |
3665 | if (info.ai_flags & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS) |
3666 | return true; |
3667 | else |
3668 | return false; |
3669 | #endif |
3670 | } |
3671 | |
3672 | // Checking for |
3673 | // |
3674 | // { |
3675 | // 'class' : 'rule', |
3676 | // 'comment' : 'For use by Apple. WARNING: administrators are advised |
3677 | // not to modify this right.', |
3678 | // 'k-of-n' : '1', |
3679 | // 'rule' : [ |
3680 | // 'is-admin', |
3681 | // 'is-developer', |
3682 | // 'authenticate-developer' |
3683 | // ] |
3684 | // } |
3685 | // |
3686 | // $ security authorizationdb read system.privilege.taskport.debug |
3687 | |
3688 | static bool developer_mode_enabled () { |
3689 | // This API only exists on macOS. |
3690 | #if TARGET_OS_OSX == 0 |
3691 | return true; |
3692 | #else |
3693 | CFDictionaryRef currentRightDict = NULL; |
3694 | const char *debug_right = "system.privilege.taskport.debug" ; |
3695 | // caller must free dictionary initialized by the following |
3696 | OSStatus status = AuthorizationRightGet(debug_right, ¤tRightDict); |
3697 | if (status != errAuthorizationSuccess) { |
3698 | // could not check authorization |
3699 | return true; |
3700 | } |
3701 | |
3702 | bool devmode_enabled = true; |
3703 | |
3704 | if (!CFDictionaryContainsKey(currentRightDict, CFSTR("k-of-n" ))) { |
3705 | devmode_enabled = false; |
3706 | } else { |
3707 | CFNumberRef item = (CFNumberRef) CFDictionaryGetValue(currentRightDict, CFSTR("k-of-n" )); |
3708 | if (item && CFGetTypeID(item) == CFNumberGetTypeID()) { |
3709 | int64_t num = 0; |
3710 | ::CFNumberGetValue(item, kCFNumberSInt64Type, &num); |
3711 | if (num != 1) { |
3712 | devmode_enabled = false; |
3713 | } |
3714 | } else { |
3715 | devmode_enabled = false; |
3716 | } |
3717 | } |
3718 | |
3719 | if (!CFDictionaryContainsKey(currentRightDict, CFSTR("class" ))) { |
3720 | devmode_enabled = false; |
3721 | } else { |
3722 | CFStringRef item = (CFStringRef) CFDictionaryGetValue(currentRightDict, CFSTR("class" )); |
3723 | if (item && CFGetTypeID(item) == CFStringGetTypeID()) { |
3724 | char tmpbuf[128]; |
3725 | if (CFStringGetCString (item, tmpbuf, sizeof(tmpbuf), CFStringGetSystemEncoding())) { |
3726 | tmpbuf[sizeof (tmpbuf) - 1] = '\0'; |
3727 | if (strcmp (tmpbuf, "rule" ) != 0) { |
3728 | devmode_enabled = false; |
3729 | } |
3730 | } else { |
3731 | devmode_enabled = false; |
3732 | } |
3733 | } else { |
3734 | devmode_enabled = false; |
3735 | } |
3736 | } |
3737 | |
3738 | if (!CFDictionaryContainsKey(currentRightDict, CFSTR("rule" ))) { |
3739 | devmode_enabled = false; |
3740 | } else { |
3741 | CFArrayRef item = (CFArrayRef) CFDictionaryGetValue(currentRightDict, CFSTR("rule" )); |
3742 | if (item && CFGetTypeID(item) == CFArrayGetTypeID()) { |
3743 | int count = ::CFArrayGetCount(item); |
3744 | CFRange range = CFRangeMake (0, count); |
3745 | if (!::CFArrayContainsValue (item, range, CFSTR("is-admin" ))) |
3746 | devmode_enabled = false; |
3747 | if (!::CFArrayContainsValue (item, range, CFSTR("is-developer" ))) |
3748 | devmode_enabled = false; |
3749 | if (!::CFArrayContainsValue (item, range, CFSTR("authenticate-developer" ))) |
3750 | devmode_enabled = false; |
3751 | } else { |
3752 | devmode_enabled = false; |
3753 | } |
3754 | } |
3755 | ::CFRelease(currentRightDict); |
3756 | |
3757 | return devmode_enabled; |
3758 | #endif // TARGET_OS_OSX |
3759 | } |
3760 | |
3761 | /* |
3762 | vAttach;pid |
3763 | |
3764 | Attach to a new process with the specified process ID. pid is a hexadecimal |
3765 | integer |
3766 | identifying the process. If the stub is currently controlling a process, it is |
3767 | killed. The attached process is stopped.This packet is only available in |
3768 | extended |
3769 | mode (see extended mode). |
3770 | |
3771 | Reply: |
3772 | "ENN" for an error |
3773 | "Any Stop Reply Packet" for success |
3774 | */ |
3775 | |
3776 | rnb_err_t RNBRemote::HandlePacket_v(const char *p) { |
3777 | if (strcmp(s1: p, s2: "vCont;c" ) == 0) { |
3778 | // Simple continue |
3779 | return RNBRemote::HandlePacket_c(p: "c" ); |
3780 | } else if (strcmp(s1: p, s2: "vCont;s" ) == 0) { |
3781 | // Simple step |
3782 | return RNBRemote::HandlePacket_s(p: "s" ); |
3783 | } else if (strstr(haystack: p, needle: "vCont" ) == p) { |
3784 | DNBThreadResumeActions thread_actions; |
3785 | char *c = const_cast<char *>(p += strlen(s: "vCont" )); |
3786 | char *c_end = c + strlen(s: c); |
3787 | if (*c == '?') |
3788 | return SendPacket(s: "vCont;c;C;s;S" ); |
3789 | |
3790 | while (c < c_end && *c == ';') { |
3791 | ++c; // Skip the semi-colon |
3792 | DNBThreadResumeAction thread_action; |
3793 | thread_action.tid = INVALID_NUB_THREAD; |
3794 | thread_action.state = eStateInvalid; |
3795 | thread_action.signal = 0; |
3796 | thread_action.addr = INVALID_NUB_ADDRESS; |
3797 | |
3798 | char action = *c++; |
3799 | |
3800 | switch (action) { |
3801 | case 'C': |
3802 | errno = 0; |
3803 | thread_action.signal = static_cast<int>(strtoul(nptr: c, endptr: &c, base: 16)); |
3804 | if (errno != 0) |
3805 | return HandlePacket_ILLFORMED( |
3806 | __FILE__, __LINE__, p, description: "Could not parse signal in vCont packet" ); |
3807 | // Fall through to next case... |
3808 | [[clang::fallthrough]]; |
3809 | case 'c': |
3810 | // Continue |
3811 | thread_action.state = eStateRunning; |
3812 | break; |
3813 | |
3814 | case 'S': |
3815 | errno = 0; |
3816 | thread_action.signal = static_cast<int>(strtoul(nptr: c, endptr: &c, base: 16)); |
3817 | if (errno != 0) |
3818 | return HandlePacket_ILLFORMED( |
3819 | __FILE__, __LINE__, p, description: "Could not parse signal in vCont packet" ); |
3820 | // Fall through to next case... |
3821 | [[clang::fallthrough]]; |
3822 | case 's': |
3823 | // Step |
3824 | thread_action.state = eStateStepping; |
3825 | break; |
3826 | |
3827 | default: |
3828 | HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
3829 | description: "Unsupported action in vCont packet" ); |
3830 | break; |
3831 | } |
3832 | if (*c == ':') { |
3833 | errno = 0; |
3834 | thread_action.tid = strtoul(nptr: ++c, endptr: &c, base: 16); |
3835 | if (errno != 0) |
3836 | return HandlePacket_ILLFORMED( |
3837 | __FILE__, __LINE__, p, |
3838 | description: "Could not parse thread number in vCont packet" ); |
3839 | } |
3840 | |
3841 | thread_actions.Append(action: thread_action); |
3842 | } |
3843 | |
3844 | // If a default action for all other threads wasn't mentioned |
3845 | // then we should stop the threads |
3846 | thread_actions.SetDefaultThreadActionIfNeeded(action: eStateStopped, signal: 0); |
3847 | DNBProcessResume(m_ctx.ProcessID(), thread_actions.GetFirst(), |
3848 | thread_actions.GetSize()); |
3849 | return rnb_success; |
3850 | } else if (strstr(haystack: p, needle: "vAttach" ) == p) { |
3851 | nub_process_t attach_pid = |
3852 | INVALID_NUB_PROCESS; // attach_pid will be set to 0 if the attach fails |
3853 | nub_process_t pid_attaching_to = |
3854 | INVALID_NUB_PROCESS; // pid_attaching_to is the original pid specified |
3855 | char err_str[1024] = {'\0'}; |
3856 | std::string attach_name; |
3857 | |
3858 | if (strstr(haystack: p, needle: "vAttachWait;" ) == p) { |
3859 | p += strlen(s: "vAttachWait;" ); |
3860 | if (!GetProcessNameFrom_vAttach(p, attach_name)) { |
3861 | return HandlePacket_ILLFORMED( |
3862 | __FILE__, __LINE__, p, description: "non-hex char in arg on 'vAttachWait' pkt" ); |
3863 | } |
3864 | DNBLog("[LaunchAttach] START %d vAttachWait for process name '%s'" , |
3865 | getpid(), attach_name.c_str()); |
3866 | const bool ignore_existing = true; |
3867 | attach_pid = DNBProcessAttachWait( |
3868 | &m_ctx, attach_name.c_str(), ignore_existing, NULL, 1000, err_str, |
3869 | sizeof(err_str), RNBRemoteShouldCancelCallback); |
3870 | |
3871 | } else if (strstr(haystack: p, needle: "vAttachOrWait;" ) == p) { |
3872 | p += strlen(s: "vAttachOrWait;" ); |
3873 | if (!GetProcessNameFrom_vAttach(p, attach_name)) { |
3874 | return HandlePacket_ILLFORMED( |
3875 | __FILE__, __LINE__, p, |
3876 | description: "non-hex char in arg on 'vAttachOrWait' pkt" ); |
3877 | } |
3878 | const bool ignore_existing = false; |
3879 | DNBLog("[LaunchAttach] START %d vAttachWaitOrWait for process name " |
3880 | "'%s'" , |
3881 | getpid(), attach_name.c_str()); |
3882 | attach_pid = DNBProcessAttachWait( |
3883 | &m_ctx, attach_name.c_str(), ignore_existing, NULL, 1000, err_str, |
3884 | sizeof(err_str), RNBRemoteShouldCancelCallback); |
3885 | } else if (strstr(haystack: p, needle: "vAttachName;" ) == p) { |
3886 | p += strlen(s: "vAttachName;" ); |
3887 | if (!GetProcessNameFrom_vAttach(p, attach_name)) { |
3888 | return HandlePacket_ILLFORMED( |
3889 | __FILE__, __LINE__, p, description: "non-hex char in arg on 'vAttachName' pkt" ); |
3890 | } |
3891 | |
3892 | DNBLog("[LaunchAttach] START %d vAttachName attach to process name " |
3893 | "'%s'" , |
3894 | getpid(), attach_name.c_str()); |
3895 | attach_pid = DNBProcessAttachByName(attach_name.c_str(), NULL, |
3896 | Context().GetIgnoredExceptions(), |
3897 | err_str, sizeof(err_str)); |
3898 | |
3899 | } else if (strstr(haystack: p, needle: "vAttach;" ) == p) { |
3900 | p += strlen(s: "vAttach;" ); |
3901 | char *end = NULL; |
3902 | pid_attaching_to = static_cast<int>( |
3903 | strtoul(nptr: p, endptr: &end, base: 16)); // PID will be in hex, so use base 16 to decode |
3904 | if (p != end && *end == '\0') { |
3905 | // Wait at most 30 second for attach |
3906 | struct timespec attach_timeout_abstime; |
3907 | DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, 30, 0); |
3908 | DNBLog("[LaunchAttach] START %d vAttach to pid %d" , getpid(), |
3909 | pid_attaching_to); |
3910 | attach_pid = DNBProcessAttach(pid_attaching_to, &attach_timeout_abstime, |
3911 | m_ctx.GetIgnoredExceptions(), |
3912 | err_str, sizeof(err_str)); |
3913 | } |
3914 | } else { |
3915 | return HandlePacket_UNIMPLEMENTED(p); |
3916 | } |
3917 | |
3918 | if (attach_pid == INVALID_NUB_PROCESS_ARCH) { |
3919 | DNBLogError("debugserver is x86_64 binary running in translation, attach " |
3920 | "failed." ); |
3921 | return SendErrorPacket(errcode: "E96" , errmsg: "debugserver is x86_64 binary running in " |
3922 | "translation, attach failed." ); |
3923 | } |
3924 | |
3925 | if (attach_pid != INVALID_NUB_PROCESS) { |
3926 | if (m_ctx.ProcessID() != attach_pid) |
3927 | m_ctx.SetProcessID(attach_pid); |
3928 | DNBLog("Successfully attached to pid %d" , attach_pid); |
3929 | // Send a stop reply packet to indicate we successfully attached! |
3930 | NotifyThatProcessStopped(); |
3931 | return rnb_success; |
3932 | } else { |
3933 | DNBLogError("Attach failed" ); |
3934 | m_ctx.LaunchStatus().SetError(-1, DNBError::Generic); |
3935 | if (err_str[0]) |
3936 | m_ctx.LaunchStatus().SetErrorString(err_str); |
3937 | else |
3938 | m_ctx.LaunchStatus().SetErrorString("attach failed" ); |
3939 | |
3940 | if (pid_attaching_to == INVALID_NUB_PROCESS && !attach_name.empty()) { |
3941 | pid_attaching_to = DNBProcessGetPIDByName(name: attach_name.c_str()); |
3942 | } |
3943 | |
3944 | // attach_pid is INVALID_NUB_PROCESS - we did not succeed in attaching |
3945 | // if the original request, pid_attaching_to, is available, see if we |
3946 | // can figure out why we couldn't attach. Return an informative error |
3947 | // string to lldb. |
3948 | |
3949 | if (pid_attaching_to != INVALID_NUB_PROCESS) { |
3950 | // The order of these checks is important. |
3951 | if (process_does_not_exist (pid: pid_attaching_to)) { |
3952 | DNBLogError("Tried to attach to pid that doesn't exist" ); |
3953 | return SendErrorPacket(errcode: "E96" , errmsg: "no such process" ); |
3954 | } |
3955 | if (process_is_already_being_debugged (pid: pid_attaching_to)) { |
3956 | DNBLogError("Tried to attach to process already being debugged" ); |
3957 | return SendErrorPacket(errcode: "E96" , errmsg: "tried to attach to " |
3958 | "process already being debugged" ); |
3959 | } |
3960 | uid_t my_uid, process_uid; |
3961 | if (attach_failed_due_to_uid_mismatch (pid: pid_attaching_to, |
3962 | my_uid, process_uid)) { |
3963 | std::string my_username = "uid " + std::to_string (val: my_uid); |
3964 | std::string process_username = "uid " + std::to_string (val: process_uid); |
3965 | struct passwd *pw = getpwuid (uid: my_uid); |
3966 | if (pw && pw->pw_name) { |
3967 | my_username = pw->pw_name; |
3968 | } |
3969 | pw = getpwuid (uid: process_uid); |
3970 | if (pw && pw->pw_name) { |
3971 | process_username = pw->pw_name; |
3972 | } |
3973 | DNBLogError("Tried to attach to process with uid mismatch" ); |
3974 | std::string msg = "tried to attach to process as user '" + |
3975 | my_username + |
3976 | "' and process is running " |
3977 | "as user '" + |
3978 | process_username + "'" ; |
3979 | return SendErrorPacket(errcode: "E96" , errmsg: msg); |
3980 | } |
3981 | if (!login_session_has_gui_access() && !developer_mode_enabled()) { |
3982 | DNBLogError("Developer mode is not enabled and this is a " |
3983 | "non-interactive session" ); |
3984 | return SendErrorPacket(errcode: "E96" , errmsg: "developer mode is " |
3985 | "not enabled on this machine " |
3986 | "and this is a non-interactive " |
3987 | "debug session." ); |
3988 | } |
3989 | if (!login_session_has_gui_access()) { |
3990 | DNBLogError("This is a non-interactive session" ); |
3991 | return SendErrorPacket(errcode: "E96" , errmsg: "this is a " |
3992 | "non-interactive debug session, " |
3993 | "cannot get permission to debug " |
3994 | "processes." ); |
3995 | } |
3996 | } |
3997 | |
3998 | std::string error_explainer = "attach failed" ; |
3999 | if (err_str[0] != '\0') { |
4000 | // This is not a super helpful message for end users |
4001 | if (strcmp (s1: err_str, s2: "unable to start the exception thread" ) == 0) { |
4002 | snprintf (s: err_str, maxlen: sizeof (err_str) - 1, |
4003 | format: "Not allowed to attach to process. Look in the console " |
4004 | "messages (Console.app), near the debugserver entries, " |
4005 | "when the attach failed. The subsystem that denied " |
4006 | "the attach permission will likely have logged an " |
4007 | "informative message about why it was denied." ); |
4008 | err_str[sizeof (err_str) - 1] = '\0'; |
4009 | } |
4010 | error_explainer += " (" ; |
4011 | error_explainer += err_str; |
4012 | error_explainer += ")" ; |
4013 | } |
4014 | DNBLogError("Attach failed: \"%s\"." , err_str); |
4015 | return SendErrorPacket(errcode: "E96" , errmsg: error_explainer); |
4016 | } |
4017 | } |
4018 | |
4019 | // All other failures come through here |
4020 | return HandlePacket_UNIMPLEMENTED(p); |
4021 | } |
4022 | |
4023 | /* 'T XX' -- status of thread |
4024 | Check if the specified thread is alive. |
4025 | The thread number is in hex? */ |
4026 | |
4027 | rnb_err_t RNBRemote::HandlePacket_T(const char *p) { |
4028 | p++; |
4029 | if (p == NULL || *p == '\0') { |
4030 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4031 | description: "No thread specified in T packet" ); |
4032 | } |
4033 | if (!m_ctx.HasValidProcessID()) { |
4034 | return SendErrorPacket(errcode: "E15" ); |
4035 | } |
4036 | errno = 0; |
4037 | nub_thread_t tid = strtoul(nptr: p, NULL, base: 16); |
4038 | if (errno != 0 && tid == 0) { |
4039 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4040 | description: "Could not parse thread number in T packet" ); |
4041 | } |
4042 | |
4043 | nub_state_t state = DNBThreadGetState(m_ctx.ProcessID(), tid); |
4044 | if (state == eStateInvalid || state == eStateExited || |
4045 | state == eStateCrashed) { |
4046 | return SendErrorPacket(errcode: "E16" ); |
4047 | } |
4048 | |
4049 | return SendPacket(s: "OK" ); |
4050 | } |
4051 | |
4052 | rnb_err_t RNBRemote::HandlePacket_z(const char *p) { |
4053 | if (p == NULL || *p == '\0') |
4054 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4055 | description: "No thread specified in z packet" ); |
4056 | |
4057 | if (!m_ctx.HasValidProcessID()) |
4058 | return SendErrorPacket(errcode: "E15" ); |
4059 | |
4060 | char packet_cmd = *p++; |
4061 | char break_type = *p++; |
4062 | |
4063 | if (*p++ != ',') |
4064 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4065 | description: "Comma separator missing in z packet" ); |
4066 | |
4067 | char *c = NULL; |
4068 | nub_process_t pid = m_ctx.ProcessID(); |
4069 | errno = 0; |
4070 | nub_addr_t addr = strtoull(nptr: p, endptr: &c, base: 16); |
4071 | if (errno != 0 && addr == 0) |
4072 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4073 | description: "Invalid address in z packet" ); |
4074 | p = c; |
4075 | if (*p++ != ',') |
4076 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4077 | description: "Comma separator missing in z packet" ); |
4078 | |
4079 | errno = 0; |
4080 | auto byte_size = strtoul(nptr: p, endptr: &c, base: 16); |
4081 | if (errno != 0 && byte_size == 0) |
4082 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4083 | description: "Invalid length in z packet" ); |
4084 | |
4085 | if (packet_cmd == 'Z') { |
4086 | // set |
4087 | switch (break_type) { |
4088 | case '0': // set software breakpoint |
4089 | case '1': // set hardware breakpoint |
4090 | { |
4091 | // gdb can send multiple Z packets for the same address and |
4092 | // these calls must be ref counted. |
4093 | bool hardware = (break_type == '1'); |
4094 | |
4095 | if (DNBBreakpointSet(pid, addr, size: byte_size, hardware)) { |
4096 | // We successfully created a breakpoint, now lets full out |
4097 | // a ref count structure with the breakID and add it to our |
4098 | // map. |
4099 | return SendPacket(s: "OK" ); |
4100 | } else { |
4101 | // We failed to set the software breakpoint |
4102 | return SendErrorPacket(errcode: "E09" ); |
4103 | } |
4104 | } break; |
4105 | |
4106 | case '2': // set write watchpoint |
4107 | case '3': // set read watchpoint |
4108 | case '4': // set access watchpoint |
4109 | { |
4110 | bool hardware = true; |
4111 | uint32_t watch_flags = 0; |
4112 | if (break_type == '2') |
4113 | watch_flags = WATCH_TYPE_WRITE; |
4114 | else if (break_type == '3') |
4115 | watch_flags = WATCH_TYPE_READ; |
4116 | else |
4117 | watch_flags = WATCH_TYPE_READ | WATCH_TYPE_WRITE; |
4118 | |
4119 | if (DNBWatchpointSet(pid, addr, size: byte_size, watch_flags, hardware)) { |
4120 | return SendPacket(s: "OK" ); |
4121 | } else { |
4122 | // We failed to set the watchpoint |
4123 | return SendErrorPacket(errcode: "E09" ); |
4124 | } |
4125 | } break; |
4126 | |
4127 | default: |
4128 | break; |
4129 | } |
4130 | } else if (packet_cmd == 'z') { |
4131 | // remove |
4132 | switch (break_type) { |
4133 | case '0': // remove software breakpoint |
4134 | case '1': // remove hardware breakpoint |
4135 | if (DNBBreakpointClear(pid, addr)) { |
4136 | return SendPacket(s: "OK" ); |
4137 | } else { |
4138 | return SendErrorPacket(errcode: "E08" ); |
4139 | } |
4140 | break; |
4141 | |
4142 | case '2': // remove write watchpoint |
4143 | case '3': // remove read watchpoint |
4144 | case '4': // remove access watchpoint |
4145 | if (DNBWatchpointClear(pid, addr)) { |
4146 | return SendPacket(s: "OK" ); |
4147 | } else { |
4148 | return SendErrorPacket(errcode: "E08" ); |
4149 | } |
4150 | break; |
4151 | |
4152 | default: |
4153 | break; |
4154 | } |
4155 | } |
4156 | return HandlePacket_UNIMPLEMENTED(p); |
4157 | } |
4158 | |
4159 | // Extract the thread number from the thread suffix that might be appended to |
4160 | // thread specific packets. This will only be enabled if |
4161 | // m_thread_suffix_supported |
4162 | // is true. |
4163 | nub_thread_t RNBRemote::(const char *p) { |
4164 | if (m_thread_suffix_supported) { |
4165 | nub_thread_t tid = INVALID_NUB_THREAD; |
4166 | if (p) { |
4167 | const char *tid_cstr = strstr(haystack: p, needle: "thread:" ); |
4168 | if (tid_cstr) { |
4169 | tid_cstr += strlen(s: "thread:" ); |
4170 | tid = strtoul(nptr: tid_cstr, NULL, base: 16); |
4171 | } |
4172 | } |
4173 | return tid; |
4174 | } |
4175 | return GetCurrentThread(); |
4176 | } |
4177 | |
4178 | /* 'p XX' |
4179 | print the contents of register X */ |
4180 | |
4181 | rnb_err_t RNBRemote::HandlePacket_p(const char *p) { |
4182 | if (g_num_reg_entries == 0) |
4183 | InitializeRegisters(); |
4184 | |
4185 | if (p == NULL || *p == '\0') { |
4186 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4187 | description: "No thread specified in p packet" ); |
4188 | } |
4189 | if (!m_ctx.HasValidProcessID()) { |
4190 | return SendErrorPacket(errcode: "E15" ); |
4191 | } |
4192 | nub_process_t pid = m_ctx.ProcessID(); |
4193 | errno = 0; |
4194 | char *tid_cstr = NULL; |
4195 | uint32_t reg = static_cast<uint32_t>(strtoul(nptr: p + 1, endptr: &tid_cstr, base: 16)); |
4196 | if (errno != 0 && reg == 0) { |
4197 | return HandlePacket_ILLFORMED( |
4198 | __FILE__, __LINE__, p, description: "Could not parse register number in p packet" ); |
4199 | } |
4200 | |
4201 | nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p: tid_cstr); |
4202 | if (tid == INVALID_NUB_THREAD) |
4203 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4204 | description: "No thread specified in p packet" ); |
4205 | |
4206 | const register_map_entry_t *reg_entry; |
4207 | |
4208 | if (reg < g_num_reg_entries) |
4209 | reg_entry = &g_reg_entries[reg]; |
4210 | else |
4211 | reg_entry = NULL; |
4212 | |
4213 | std::ostringstream ostrm; |
4214 | if (reg_entry == NULL) { |
4215 | DNBLogError( |
4216 | "RNBRemote::HandlePacket_p(%s): unknown register number %u requested\n" , |
4217 | p, reg); |
4218 | ostrm << "00000000" ; |
4219 | } else if (reg_entry->nub_info.reg == (uint32_t)-1) { |
4220 | if (reg_entry->nub_info.size > 0) { |
4221 | std::vector<uint8_t> zeros(reg_entry->nub_info.size, '\0'); |
4222 | append_hex_value(ostrm, buf: zeros.data(), buf_size: zeros.size(), swap: false); |
4223 | } |
4224 | } else { |
4225 | register_value_in_hex_fixed_width(ostrm, pid, tid, reg: reg_entry, NULL); |
4226 | } |
4227 | return SendPacket(s: ostrm.str()); |
4228 | } |
4229 | |
4230 | /* 'Pnn=rrrrr' |
4231 | Set register number n to value r. |
4232 | n and r are hex strings. */ |
4233 | |
4234 | rnb_err_t RNBRemote::HandlePacket_P(const char *p) { |
4235 | if (g_num_reg_entries == 0) |
4236 | InitializeRegisters(); |
4237 | |
4238 | if (p == NULL || *p == '\0') { |
4239 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, description: "Empty P packet" ); |
4240 | } |
4241 | if (!m_ctx.HasValidProcessID()) { |
4242 | return SendErrorPacket(errcode: "E28" ); |
4243 | } |
4244 | |
4245 | nub_process_t pid = m_ctx.ProcessID(); |
4246 | |
4247 | StdStringExtractor packet(p); |
4248 | |
4249 | const char cmd_char = packet.GetChar(); |
4250 | // Register ID is always in big endian |
4251 | const uint32_t reg = packet.GetHexMaxU32(little_endian: false, UINT32_MAX); |
4252 | const char equal_char = packet.GetChar(); |
4253 | |
4254 | if (cmd_char != 'P') |
4255 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4256 | description: "Improperly formed P packet" ); |
4257 | |
4258 | if (reg == UINT32_MAX) |
4259 | return SendErrorPacket(errcode: "E29" ); |
4260 | |
4261 | if (equal_char != '=') |
4262 | return SendErrorPacket(errcode: "E30" ); |
4263 | |
4264 | const register_map_entry_t *reg_entry; |
4265 | |
4266 | if (reg >= g_num_reg_entries) |
4267 | return SendErrorPacket(errcode: "E47" ); |
4268 | |
4269 | reg_entry = &g_reg_entries[reg]; |
4270 | |
4271 | if (reg_entry->nub_info.set == (uint32_t)-1 && |
4272 | reg_entry->nub_info.reg == (uint32_t)-1) { |
4273 | DNBLogError( |
4274 | "RNBRemote::HandlePacket_P(%s): unknown register number %u requested\n" , |
4275 | p, reg); |
4276 | return SendErrorPacket(errcode: "E48" ); |
4277 | } |
4278 | |
4279 | DNBRegisterValue reg_value; |
4280 | reg_value.info = reg_entry->nub_info; |
4281 | packet.GetHexBytes(dst: reg_value.value.v_sint8, dst_len: reg_entry->nub_info.size, fail_fill_value: 0xcc); |
4282 | |
4283 | nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p); |
4284 | if (tid == INVALID_NUB_THREAD) |
4285 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4286 | description: "No thread specified in p packet" ); |
4287 | |
4288 | if (!DNBThreadSetRegisterValueByID(pid, tid, set: reg_entry->nub_info.set, |
4289 | reg: reg_entry->nub_info.reg, value: ®_value)) { |
4290 | return SendErrorPacket(errcode: "E32" ); |
4291 | } |
4292 | return SendPacket(s: "OK" ); |
4293 | } |
4294 | |
4295 | /* 'c [addr]' |
4296 | Continue, optionally from a specified address. */ |
4297 | |
4298 | rnb_err_t RNBRemote::HandlePacket_c(const char *p) { |
4299 | const nub_process_t pid = m_ctx.ProcessID(); |
4300 | |
4301 | if (pid == INVALID_NUB_PROCESS) |
4302 | return SendErrorPacket(errcode: "E23" ); |
4303 | |
4304 | DNBThreadResumeAction action = {INVALID_NUB_THREAD, .state: eStateRunning, .signal: 0, |
4305 | INVALID_NUB_ADDRESS}; |
4306 | |
4307 | if (*(p + 1) != '\0') { |
4308 | action.tid = GetContinueThread(); |
4309 | errno = 0; |
4310 | action.addr = strtoull(nptr: p + 1, NULL, base: 16); |
4311 | if (errno != 0 && action.addr == 0) |
4312 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4313 | description: "Could not parse address in c packet" ); |
4314 | } |
4315 | |
4316 | DNBThreadResumeActions thread_actions; |
4317 | thread_actions.Append(action); |
4318 | thread_actions.SetDefaultThreadActionIfNeeded(action: eStateRunning, signal: 0); |
4319 | if (!DNBProcessResume(pid, actions: thread_actions.GetFirst(), |
4320 | num_actions: thread_actions.GetSize())) |
4321 | return SendErrorPacket(errcode: "E25" ); |
4322 | // Don't send an "OK" packet; response is the stopped/exited message. |
4323 | return rnb_success; |
4324 | } |
4325 | |
4326 | rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) { |
4327 | /* This packet will find memory attributes (e.g. readable, writable, |
4328 | executable, stack, jitted code) |
4329 | for the memory region containing a given address and return that |
4330 | information. |
4331 | |
4332 | Users of this packet must be prepared for three results: |
4333 | |
4334 | Region information is returned |
4335 | Region information is unavailable for this address because the address |
4336 | is in unmapped memory |
4337 | Region lookup cannot be performed on this platform or process is not |
4338 | yet launched |
4339 | This packet isn't implemented |
4340 | |
4341 | Examples of use: |
4342 | qMemoryRegionInfo:3a55140 |
4343 | start:3a50000,size:100000,permissions:rwx |
4344 | |
4345 | qMemoryRegionInfo:0 |
4346 | error:address in unmapped region |
4347 | |
4348 | qMemoryRegionInfo:3a551140 (on a different platform) |
4349 | error:region lookup cannot be performed |
4350 | |
4351 | qMemoryRegionInfo |
4352 | OK // this packet is implemented by the remote nub |
4353 | */ |
4354 | |
4355 | p += sizeof("qMemoryRegionInfo" ) - 1; |
4356 | if (*p == '\0') |
4357 | return SendPacket(s: "OK" ); |
4358 | if (*p++ != ':') |
4359 | return SendErrorPacket(errcode: "E67" ); |
4360 | if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) |
4361 | p += 2; |
4362 | |
4363 | errno = 0; |
4364 | uint64_t address = strtoul(nptr: p, NULL, base: 16); |
4365 | if (errno != 0 && address == 0) { |
4366 | return HandlePacket_ILLFORMED( |
4367 | __FILE__, __LINE__, p, description: "Invalid address in qMemoryRegionInfo packet" ); |
4368 | } |
4369 | |
4370 | DNBRegionInfo region_info; |
4371 | DNBProcessMemoryRegionInfo(m_ctx.ProcessID(), address, ®ion_info); |
4372 | std::ostringstream ostrm; |
4373 | |
4374 | // start:3a50000,size:100000,permissions:rwx |
4375 | ostrm << "start:" << std::hex << region_info.addr << ';'; |
4376 | |
4377 | if (region_info.size > 0) |
4378 | ostrm << "size:" << std::hex << region_info.size << ';'; |
4379 | |
4380 | if (region_info.permissions) { |
4381 | ostrm << "permissions:" ; |
4382 | |
4383 | if (region_info.permissions & eMemoryPermissionsReadable) |
4384 | ostrm << 'r'; |
4385 | if (region_info.permissions & eMemoryPermissionsWritable) |
4386 | ostrm << 'w'; |
4387 | if (region_info.permissions & eMemoryPermissionsExecutable) |
4388 | ostrm << 'x'; |
4389 | ostrm << ';'; |
4390 | |
4391 | ostrm << "dirty-pages:" ; |
4392 | if (region_info.dirty_pages.size() > 0) { |
4393 | bool first = true; |
4394 | for (nub_addr_t addr : region_info.dirty_pages) { |
4395 | if (!first) |
4396 | ostrm << "," ; |
4397 | first = false; |
4398 | ostrm << std::hex << addr; |
4399 | } |
4400 | } |
4401 | ostrm << ";" ; |
4402 | if (!region_info.vm_types.empty()) { |
4403 | ostrm << "type:" ; |
4404 | for (size_t i = 0; i < region_info.vm_types.size(); i++) { |
4405 | if (i) |
4406 | ostrm << "," ; |
4407 | ostrm << region_info.vm_types[i]; |
4408 | } |
4409 | ostrm << ";" ; |
4410 | } |
4411 | } |
4412 | return SendPacket(s: ostrm.str()); |
4413 | } |
4414 | |
4415 | // qGetProfileData;scan_type:0xYYYYYYY |
4416 | rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) { |
4417 | nub_process_t pid = m_ctx.ProcessID(); |
4418 | if (pid == INVALID_NUB_PROCESS) |
4419 | return SendPacket(s: "OK" ); |
4420 | |
4421 | StdStringExtractor packet(p += sizeof("qGetProfileData" )); |
4422 | DNBProfileDataScanType scan_type = eProfileAll; |
4423 | std::string name; |
4424 | std::string value; |
4425 | while (packet.GetNameColonValue(name, value)) { |
4426 | if (name == "scan_type" ) { |
4427 | std::istringstream iss(value); |
4428 | uint32_t int_value = 0; |
4429 | if (iss >> std::hex >> int_value) { |
4430 | scan_type = (DNBProfileDataScanType)int_value; |
4431 | } |
4432 | } |
4433 | } |
4434 | |
4435 | std::string data = DNBProcessGetProfileData(pid, scanType: scan_type); |
4436 | if (!data.empty()) { |
4437 | return SendPacket(s: data); |
4438 | } else { |
4439 | return SendPacket(s: "OK" ); |
4440 | } |
4441 | } |
4442 | |
4443 | // QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;scan_type:0xYYYYYYY |
4444 | rnb_err_t RNBRemote::HandlePacket_SetEnableAsyncProfiling(const char *p) { |
4445 | nub_process_t pid = m_ctx.ProcessID(); |
4446 | if (pid == INVALID_NUB_PROCESS) |
4447 | return SendPacket(s: "OK" ); |
4448 | |
4449 | StdStringExtractor packet(p += sizeof("QSetEnableAsyncProfiling" )); |
4450 | bool enable = false; |
4451 | uint64_t interval_usec = 0; |
4452 | DNBProfileDataScanType scan_type = eProfileAll; |
4453 | std::string name; |
4454 | std::string value; |
4455 | while (packet.GetNameColonValue(name, value)) { |
4456 | if (name == "enable" ) { |
4457 | enable = strtoul(nptr: value.c_str(), NULL, base: 10) > 0; |
4458 | } else if (name == "interval_usec" ) { |
4459 | interval_usec = strtoul(nptr: value.c_str(), NULL, base: 10); |
4460 | } else if (name == "scan_type" ) { |
4461 | std::istringstream iss(value); |
4462 | uint32_t int_value = 0; |
4463 | if (iss >> std::hex >> int_value) { |
4464 | scan_type = (DNBProfileDataScanType)int_value; |
4465 | } |
4466 | } |
4467 | } |
4468 | |
4469 | if (interval_usec == 0) { |
4470 | enable = false; |
4471 | } |
4472 | |
4473 | DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec, scan_type); |
4474 | return SendPacket(s: "OK" ); |
4475 | } |
4476 | |
4477 | // QEnableCompression:type:<COMPRESSION-TYPE>; |
4478 | // |
4479 | // type: must be a type previously reported by the qXfer:features: |
4480 | // SupportedCompressions list |
4481 | |
4482 | rnb_err_t RNBRemote::HandlePacket_QEnableCompression(const char *p) { |
4483 | p += sizeof("QEnableCompression:" ) - 1; |
4484 | |
4485 | if (strstr(haystack: p, needle: "type:zlib-deflate;" ) != nullptr) { |
4486 | EnableCompressionNextSendPacket(compression_types::zlib_deflate); |
4487 | return SendPacket(s: "OK" ); |
4488 | } else if (strstr(haystack: p, needle: "type:lz4;" ) != nullptr) { |
4489 | EnableCompressionNextSendPacket(compression_types::lz4); |
4490 | return SendPacket(s: "OK" ); |
4491 | } else if (strstr(haystack: p, needle: "type:lzma;" ) != nullptr) { |
4492 | EnableCompressionNextSendPacket(compression_types::lzma); |
4493 | return SendPacket(s: "OK" ); |
4494 | } else if (strstr(haystack: p, needle: "type:lzfse;" ) != nullptr) { |
4495 | EnableCompressionNextSendPacket(compression_types::lzfse); |
4496 | return SendPacket(s: "OK" ); |
4497 | } |
4498 | |
4499 | return SendErrorPacket(errcode: "E88" ); |
4500 | } |
4501 | |
4502 | rnb_err_t RNBRemote::HandlePacket_qSpeedTest(const char *p) { |
4503 | p += strlen(s: "qSpeedTest:response_size:" ); |
4504 | char *end = NULL; |
4505 | errno = 0; |
4506 | uint64_t response_size = ::strtoul(nptr: p, endptr: &end, base: 16); |
4507 | if (errno != 0) |
4508 | return HandlePacket_ILLFORMED( |
4509 | __FILE__, __LINE__, p, |
4510 | description: "Didn't find response_size value at right offset" ); |
4511 | else if (*end == ';') { |
4512 | static char g_data[4 * 1024 * 1024 + 16]; |
4513 | strcpy(dest: g_data, src: "data:" ); |
4514 | memset(s: g_data + 5, c: 'a', n: response_size); |
4515 | g_data[response_size + 5] = '\0'; |
4516 | return SendPacket(s: g_data); |
4517 | } else { |
4518 | return SendErrorPacket(errcode: "E79" ); |
4519 | } |
4520 | } |
4521 | |
4522 | rnb_err_t RNBRemote::HandlePacket_WatchpointSupportInfo(const char *p) { |
4523 | /* This packet simply returns the number of supported hardware watchpoints. |
4524 | |
4525 | Examples of use: |
4526 | qWatchpointSupportInfo: |
4527 | num:4 |
4528 | |
4529 | qWatchpointSupportInfo |
4530 | OK // this packet is implemented by the remote nub |
4531 | */ |
4532 | |
4533 | p += sizeof("qWatchpointSupportInfo" ) - 1; |
4534 | if (*p == '\0') |
4535 | return SendPacket(s: "OK" ); |
4536 | if (*p++ != ':') |
4537 | return SendErrorPacket(errcode: "E67" ); |
4538 | |
4539 | errno = 0; |
4540 | uint32_t num = DNBWatchpointGetNumSupportedHWP(m_ctx.ProcessID()); |
4541 | std::ostringstream ostrm; |
4542 | |
4543 | // size:4 |
4544 | ostrm << "num:" << std::dec << num << ';'; |
4545 | return SendPacket(s: ostrm.str()); |
4546 | } |
4547 | |
4548 | /* 'C sig [;addr]' |
4549 | Resume with signal sig, optionally at address addr. */ |
4550 | |
4551 | rnb_err_t RNBRemote::HandlePacket_C(const char *p) { |
4552 | const nub_process_t pid = m_ctx.ProcessID(); |
4553 | |
4554 | if (pid == INVALID_NUB_PROCESS) |
4555 | return SendErrorPacket(errcode: "E36" ); |
4556 | |
4557 | DNBThreadResumeAction action = {INVALID_NUB_THREAD, .state: eStateRunning, .signal: 0, |
4558 | INVALID_NUB_ADDRESS}; |
4559 | int process_signo = -1; |
4560 | if (*(p + 1) != '\0') { |
4561 | action.tid = GetContinueThread(); |
4562 | char *end = NULL; |
4563 | errno = 0; |
4564 | process_signo = static_cast<int>(strtoul(nptr: p + 1, endptr: &end, base: 16)); |
4565 | if (errno != 0) |
4566 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4567 | description: "Could not parse signal in C packet" ); |
4568 | else if (*end == ';') { |
4569 | errno = 0; |
4570 | action.addr = strtoull(nptr: end + 1, NULL, base: 16); |
4571 | if (errno != 0 && action.addr == 0) |
4572 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4573 | description: "Could not parse address in C packet" ); |
4574 | } |
4575 | } |
4576 | |
4577 | DNBThreadResumeActions thread_actions; |
4578 | thread_actions.Append(action); |
4579 | thread_actions.SetDefaultThreadActionIfNeeded(action: eStateRunning, signal: action.signal); |
4580 | if (!DNBProcessSignal(pid, signal: process_signo)) |
4581 | return SendErrorPacket(errcode: "E52" ); |
4582 | if (!DNBProcessResume(pid, actions: thread_actions.GetFirst(), |
4583 | num_actions: thread_actions.GetSize())) |
4584 | return SendErrorPacket(errcode: "E38" ); |
4585 | /* Don't send an "OK" packet; response is the stopped/exited message. */ |
4586 | return rnb_success; |
4587 | } |
4588 | |
4589 | // 'D' packet |
4590 | // Detach from gdb. |
4591 | rnb_err_t RNBRemote::HandlePacket_D(const char *p) { |
4592 | if (m_ctx.HasValidProcessID()) { |
4593 | DNBLog("detaching from pid %u due to D packet" , m_ctx.ProcessID()); |
4594 | if (DNBProcessDetach(m_ctx.ProcessID())) |
4595 | SendPacket(s: "OK" ); |
4596 | else { |
4597 | DNBLog("error while detaching from pid %u due to D packet" , |
4598 | m_ctx.ProcessID()); |
4599 | SendErrorPacket(errcode: "E01" ); |
4600 | } |
4601 | } else { |
4602 | SendErrorPacket(errcode: "E04" ); |
4603 | } |
4604 | return rnb_success; |
4605 | } |
4606 | |
4607 | /* 'k' |
4608 | Kill the inferior process. */ |
4609 | |
4610 | rnb_err_t RNBRemote::HandlePacket_k(const char *p) { |
4611 | DNBLog("Got a 'k' packet, killing the inferior process." ); |
4612 | // No response to should be sent to the kill packet |
4613 | if (m_ctx.HasValidProcessID()) |
4614 | DNBProcessKill(m_ctx.ProcessID()); |
4615 | SendPacket(s: "X09" ); |
4616 | return rnb_success; |
4617 | } |
4618 | |
4619 | rnb_err_t RNBRemote::HandlePacket_stop_process(const char *p) { |
4620 | //#define TEST_EXIT_ON_INTERRUPT // This should only be uncommented to test |
4621 | //exiting on interrupt |
4622 | #if defined(TEST_EXIT_ON_INTERRUPT) |
4623 | rnb_err_t err = HandlePacket_k(p); |
4624 | m_comm.Disconnect(true); |
4625 | return err; |
4626 | #else |
4627 | if (!DNBProcessInterrupt(m_ctx.ProcessID())) { |
4628 | // If we failed to interrupt the process, then send a stop |
4629 | // reply packet as the process was probably already stopped |
4630 | DNBLogThreaded("RNBRemote::HandlePacket_stop_process() sending extra stop " |
4631 | "reply because DNBProcessInterrupt returned false" ); |
4632 | HandlePacket_last_signal(NULL); |
4633 | } |
4634 | return rnb_success; |
4635 | #endif |
4636 | } |
4637 | |
4638 | /* 's' |
4639 | Step the inferior process. */ |
4640 | |
4641 | rnb_err_t RNBRemote::HandlePacket_s(const char *p) { |
4642 | const nub_process_t pid = m_ctx.ProcessID(); |
4643 | if (pid == INVALID_NUB_PROCESS) |
4644 | return SendErrorPacket(errcode: "E32" ); |
4645 | |
4646 | // Hardware supported stepping not supported on arm |
4647 | nub_thread_t tid = GetContinueThread(); |
4648 | if (tid == 0 || tid == (nub_thread_t)-1) |
4649 | tid = GetCurrentThread(); |
4650 | |
4651 | if (tid == INVALID_NUB_THREAD) |
4652 | return SendErrorPacket(errcode: "E33" ); |
4653 | |
4654 | DNBThreadResumeActions thread_actions; |
4655 | thread_actions.AppendAction(tid, state: eStateStepping); |
4656 | |
4657 | // Make all other threads stop when we are stepping |
4658 | thread_actions.SetDefaultThreadActionIfNeeded(action: eStateStopped, signal: 0); |
4659 | if (!DNBProcessResume(pid, actions: thread_actions.GetFirst(), |
4660 | num_actions: thread_actions.GetSize())) |
4661 | return SendErrorPacket(errcode: "E49" ); |
4662 | // Don't send an "OK" packet; response is the stopped/exited message. |
4663 | return rnb_success; |
4664 | } |
4665 | |
4666 | /* 'S sig [;addr]' |
4667 | Step with signal sig, optionally at address addr. */ |
4668 | |
4669 | rnb_err_t RNBRemote::HandlePacket_S(const char *p) { |
4670 | const nub_process_t pid = m_ctx.ProcessID(); |
4671 | if (pid == INVALID_NUB_PROCESS) |
4672 | return SendErrorPacket(errcode: "E36" ); |
4673 | |
4674 | DNBThreadResumeAction action = {INVALID_NUB_THREAD, .state: eStateStepping, .signal: 0, |
4675 | INVALID_NUB_ADDRESS}; |
4676 | |
4677 | if (*(p + 1) != '\0') { |
4678 | char *end = NULL; |
4679 | errno = 0; |
4680 | action.signal = static_cast<int>(strtoul(nptr: p + 1, endptr: &end, base: 16)); |
4681 | if (errno != 0) |
4682 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4683 | description: "Could not parse signal in S packet" ); |
4684 | else if (*end == ';') { |
4685 | errno = 0; |
4686 | action.addr = strtoull(nptr: end + 1, NULL, base: 16); |
4687 | if (errno != 0 && action.addr == 0) { |
4688 | return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, |
4689 | description: "Could not parse address in S packet" ); |
4690 | } |
4691 | } |
4692 | } |
4693 | |
4694 | action.tid = GetContinueThread(); |
4695 | if (action.tid == 0 || action.tid == (nub_thread_t)-1) |
4696 | return SendErrorPacket(errcode: "E40" ); |
4697 | |
4698 | nub_state_t tstate = DNBThreadGetState(pid, tid: action.tid); |
4699 | if (tstate == eStateInvalid || tstate == eStateExited) |
4700 | return SendErrorPacket(errcode: "E37" ); |
4701 | |
4702 | DNBThreadResumeActions thread_actions; |
4703 | thread_actions.Append(action); |
4704 | |
4705 | // Make all other threads stop when we are stepping |
4706 | thread_actions.SetDefaultThreadActionIfNeeded(action: eStateStopped, signal: 0); |
4707 | if (!DNBProcessResume(pid, actions: thread_actions.GetFirst(), |
4708 | num_actions: thread_actions.GetSize())) |
4709 | return SendErrorPacket(errcode: "E39" ); |
4710 | |
4711 | // Don't send an "OK" packet; response is the stopped/exited message. |
4712 | return rnb_success; |
4713 | } |
4714 | |
4715 | static const char *GetArchName(const uint32_t cputype, |
4716 | const uint32_t cpusubtype) { |
4717 | switch (cputype) { |
4718 | case CPU_TYPE_ARM: |
4719 | switch (cpusubtype) { |
4720 | case 5: |
4721 | return "armv4" ; |
4722 | case 6: |
4723 | return "armv6" ; |
4724 | case 7: |
4725 | return "armv5t" ; |
4726 | case 8: |
4727 | return "xscale" ; |
4728 | case 9: |
4729 | return "armv7" ; |
4730 | case 10: |
4731 | return "armv7f" ; |
4732 | case 11: |
4733 | return "armv7s" ; |
4734 | case 12: |
4735 | return "armv7k" ; |
4736 | case 14: |
4737 | return "armv6m" ; |
4738 | case 15: |
4739 | return "armv7m" ; |
4740 | case 16: |
4741 | return "armv7em" ; |
4742 | default: |
4743 | return "arm" ; |
4744 | } |
4745 | break; |
4746 | case CPU_TYPE_ARM64: |
4747 | return "arm64" ; |
4748 | case CPU_TYPE_ARM64_32: |
4749 | return "arm64_32" ; |
4750 | case CPU_TYPE_I386: |
4751 | return "i386" ; |
4752 | case CPU_TYPE_X86_64: |
4753 | switch (cpusubtype) { |
4754 | default: |
4755 | return "x86_64" ; |
4756 | case 8: |
4757 | return "x86_64h" ; |
4758 | } |
4759 | break; |
4760 | } |
4761 | return NULL; |
4762 | } |
4763 | |
4764 | static bool GetHostCPUType(uint32_t &cputype, uint32_t &cpusubtype, |
4765 | uint32_t &is_64_bit_capable, bool &promoted_to_64) { |
4766 | static uint32_t g_host_cputype = 0; |
4767 | static uint32_t g_host_cpusubtype = 0; |
4768 | static uint32_t g_is_64_bit_capable = 0; |
4769 | static bool g_promoted_to_64 = false; |
4770 | |
4771 | if (g_host_cputype == 0) { |
4772 | g_promoted_to_64 = false; |
4773 | size_t len = sizeof(uint32_t); |
4774 | if (::sysctlbyname("hw.cputype" , &g_host_cputype, &len, NULL, 0) == 0) { |
4775 | len = sizeof(uint32_t); |
4776 | if (::sysctlbyname("hw.cpu64bit_capable" , &g_is_64_bit_capable, &len, |
4777 | NULL, 0) == 0) { |
4778 | if (g_is_64_bit_capable && ((g_host_cputype & CPU_ARCH_ABI64) == 0)) { |
4779 | g_promoted_to_64 = true; |
4780 | g_host_cputype |= CPU_ARCH_ABI64; |
4781 | } |
4782 | } |
4783 | #if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
4784 | if (g_host_cputype == CPU_TYPE_ARM64 && sizeof (void*) == 4) |
4785 | g_host_cputype = CPU_TYPE_ARM64_32; |
4786 | #endif |
4787 | } |
4788 | |
4789 | len = sizeof(uint32_t); |
4790 | if (::sysctlbyname("hw.cpusubtype" , &g_host_cpusubtype, &len, NULL, 0) == |
4791 | 0) { |
4792 | if (g_promoted_to_64 && g_host_cputype == CPU_TYPE_X86_64 && |
4793 | g_host_cpusubtype == CPU_SUBTYPE_486) |
4794 | g_host_cpusubtype = CPU_SUBTYPE_X86_64_ALL; |
4795 | } |
4796 | #if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
4797 | // on arm64_32 devices, the machine's native cpu type is |
4798 | // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e. |
4799 | // But we change the cputype to CPU_TYPE_ARM64_32 because |
4800 | // the user processes are all ILP32 processes today. |
4801 | // We also need to rewrite the cpusubtype so we vend |
4802 | // a valid cputype + cpusubtype combination. |
4803 | if (g_host_cputype == CPU_TYPE_ARM64_32) |
4804 | g_host_cpusubtype = CPU_SUBTYPE_ARM64_32_V8; |
4805 | #endif |
4806 | } |
4807 | |
4808 | cputype = g_host_cputype; |
4809 | cpusubtype = g_host_cpusubtype; |
4810 | is_64_bit_capable = g_is_64_bit_capable; |
4811 | promoted_to_64 = g_promoted_to_64; |
4812 | return g_host_cputype != 0; |
4813 | } |
4814 | |
4815 | rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) { |
4816 | std::ostringstream strm; |
4817 | |
4818 | uint32_t cputype = 0; |
4819 | uint32_t cpusubtype = 0; |
4820 | uint32_t is_64_bit_capable = 0; |
4821 | bool promoted_to_64 = false; |
4822 | if (GetHostCPUType(cputype, cpusubtype, is_64_bit_capable, promoted_to_64)) { |
4823 | strm << "cputype:" << std::dec << cputype << ';'; |
4824 | strm << "cpusubtype:" << std::dec << cpusubtype << ';'; |
4825 | } |
4826 | |
4827 | uint32_t addressing_bits = 0; |
4828 | if (DNBGetAddressingBits(addressing_bits)) { |
4829 | strm << "addressing_bits:" << std::dec << addressing_bits << ';'; |
4830 | } |
4831 | |
4832 | // The OS in the triple should be "ios" or "macosx" which doesn't match our |
4833 | // "Darwin" which gets returned from "kern.ostype", so we need to hardcode |
4834 | // this for now. |
4835 | if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64 |
4836 | || cputype == CPU_TYPE_ARM64_32) { |
4837 | #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 |
4838 | strm << "ostype:tvos;" ; |
4839 | #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
4840 | strm << "ostype:watchos;" ; |
4841 | #elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 |
4842 | strm << "ostype:bridgeos;" ; |
4843 | #elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1 |
4844 | strm << "ostype:macosx;" ; |
4845 | #elif defined(TARGET_OS_XR) && TARGET_OS_XR == 1 |
4846 | strm << "ostype:xros;" ; |
4847 | #else |
4848 | strm << "ostype:ios;" ; |
4849 | #endif |
4850 | |
4851 | // On armv7 we use "synchronous" watchpoints which means the exception is |
4852 | // delivered before the instruction executes. |
4853 | strm << "watchpoint_exceptions_received:before;" ; |
4854 | } else { |
4855 | strm << "ostype:macosx;" ; |
4856 | strm << "watchpoint_exceptions_received:after;" ; |
4857 | } |
4858 | // char ostype[64]; |
4859 | // len = sizeof(ostype); |
4860 | // if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0) |
4861 | // { |
4862 | // len = strlen(ostype); |
4863 | // std::transform (ostype, ostype + len, ostype, tolower); |
4864 | // strm << "ostype:" << std::dec << ostype << ';'; |
4865 | // } |
4866 | |
4867 | strm << "vendor:apple;" ; |
4868 | |
4869 | uint64_t major, minor, patch; |
4870 | if (DNBGetOSVersionNumbers(major: &major, minor: &minor, patch: &patch)) { |
4871 | strm << "os_version:" << major << "." << minor; |
4872 | if (patch != UINT64_MAX) |
4873 | strm << "." << patch; |
4874 | strm << ";" ; |
4875 | } |
4876 | |
4877 | std::string maccatalyst_version = DNBGetMacCatalystVersionString(); |
4878 | if (!maccatalyst_version.empty() && |
4879 | std::all_of(first: maccatalyst_version.begin(), last: maccatalyst_version.end(), |
4880 | pred: [](char c) { return (c >= '0' && c <= '9') || c == '.'; })) |
4881 | strm << "maccatalyst_version:" << maccatalyst_version << ";" ; |
4882 | |
4883 | #if defined(__LITTLE_ENDIAN__) |
4884 | strm << "endian:little;" ; |
4885 | #elif defined(__BIG_ENDIAN__) |
4886 | strm << "endian:big;" ; |
4887 | #elif defined(__PDP_ENDIAN__) |
4888 | strm << "endian:pdp;" ; |
4889 | #endif |
4890 | |
4891 | if (promoted_to_64) |
4892 | strm << "ptrsize:8;" ; |
4893 | else |
4894 | strm << "ptrsize:" << std::dec << sizeof(void *) << ';'; |
4895 | |
4896 | #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
4897 | strm << "default_packet_timeout:10;" ; |
4898 | #endif |
4899 | |
4900 | strm << "vm-page-size:" << std::dec << vm_page_size << ";" ; |
4901 | |
4902 | return SendPacket(s: strm.str()); |
4903 | } |
4904 | |
4905 | void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name, |
4906 | bool has_attributes) { |
4907 | if (indent) |
4908 | s << INDENT_WITH_SPACES(indent); |
4909 | s << '<' << name; |
4910 | if (!has_attributes) |
4911 | s << '>' << std::endl; |
4912 | } |
4913 | |
4914 | void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) { |
4915 | if (empty) |
4916 | s << '/'; |
4917 | s << '>' << std::endl; |
4918 | } |
4919 | |
4920 | void XMLElementEnd(std::ostringstream &s, uint32_t indent, const char *name) { |
4921 | if (indent) |
4922 | s << INDENT_WITH_SPACES(indent); |
4923 | s << '<' << '/' << name << '>' << std::endl; |
4924 | } |
4925 | |
4926 | void XMLElementWithStringValue(std::ostringstream &s, uint32_t indent, |
4927 | const char *name, const char *value, |
4928 | bool close = true) { |
4929 | if (value) { |
4930 | if (indent) |
4931 | s << INDENT_WITH_SPACES(indent); |
4932 | s << '<' << name << '>' << value; |
4933 | if (close) |
4934 | XMLElementEnd(s, indent: 0, name); |
4935 | } |
4936 | } |
4937 | |
4938 | void XMLElementWithUnsignedValue(std::ostringstream &s, uint32_t indent, |
4939 | const char *name, uint64_t value, |
4940 | bool close = true) { |
4941 | if (indent) |
4942 | s << INDENT_WITH_SPACES(indent); |
4943 | |
4944 | s << '<' << name << '>' << DECIMAL << value; |
4945 | if (close) |
4946 | XMLElementEnd(s, indent: 0, name); |
4947 | } |
4948 | |
4949 | void XMLAttributeString(std::ostringstream &s, const char *name, |
4950 | const char *value, const char *default_value = NULL) { |
4951 | if (value) { |
4952 | if (default_value && strcmp(s1: value, s2: default_value) == 0) |
4953 | return; // No need to emit the attribute because it matches the default |
4954 | // value |
4955 | s << ' ' << name << "=\"" << value << "\"" ; |
4956 | } |
4957 | } |
4958 | |
4959 | void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name, |
4960 | uint64_t value) { |
4961 | s << ' ' << name << "=\"" << DECIMAL << value << "\"" ; |
4962 | } |
4963 | |
4964 | void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num, |
4965 | nub_size_t num_reg_sets, |
4966 | const DNBRegisterSetInfo *reg_set_info, |
4967 | const register_map_entry_t ®) { |
4968 | const char *default_lldb_encoding = "uint" ; |
4969 | const char *lldb_encoding = default_lldb_encoding; |
4970 | const char *gdb_group = "general" ; |
4971 | const char *default_gdb_type = "int" ; |
4972 | const char *gdb_type = default_gdb_type; |
4973 | const char *default_lldb_format = "hex" ; |
4974 | const char *lldb_format = default_lldb_format; |
4975 | |
4976 | switch (reg.nub_info.type) { |
4977 | case Uint: |
4978 | lldb_encoding = "uint" ; |
4979 | break; |
4980 | case Sint: |
4981 | lldb_encoding = "sint" ; |
4982 | break; |
4983 | case IEEE754: |
4984 | lldb_encoding = "ieee754" ; |
4985 | if (reg.nub_info.set > 0) |
4986 | gdb_group = "float" ; |
4987 | break; |
4988 | case Vector: |
4989 | lldb_encoding = "vector" ; |
4990 | if (reg.nub_info.set > 0) |
4991 | gdb_group = "vector" ; |
4992 | break; |
4993 | } |
4994 | |
4995 | switch (reg.nub_info.format) { |
4996 | case Binary: |
4997 | lldb_format = "binary" ; |
4998 | break; |
4999 | case Decimal: |
5000 | lldb_format = "decimal" ; |
5001 | break; |
5002 | case Hex: |
5003 | lldb_format = "hex" ; |
5004 | break; |
5005 | case Float: |
5006 | gdb_type = "float" ; |
5007 | lldb_format = "float" ; |
5008 | break; |
5009 | case VectorOfSInt8: |
5010 | gdb_type = "float" ; |
5011 | lldb_format = "vector-sint8" ; |
5012 | break; |
5013 | case VectorOfUInt8: |
5014 | gdb_type = "float" ; |
5015 | lldb_format = "vector-uint8" ; |
5016 | break; |
5017 | case VectorOfSInt16: |
5018 | gdb_type = "float" ; |
5019 | lldb_format = "vector-sint16" ; |
5020 | break; |
5021 | case VectorOfUInt16: |
5022 | gdb_type = "float" ; |
5023 | lldb_format = "vector-uint16" ; |
5024 | break; |
5025 | case VectorOfSInt32: |
5026 | gdb_type = "float" ; |
5027 | lldb_format = "vector-sint32" ; |
5028 | break; |
5029 | case VectorOfUInt32: |
5030 | gdb_type = "float" ; |
5031 | lldb_format = "vector-uint32" ; |
5032 | break; |
5033 | case VectorOfFloat32: |
5034 | gdb_type = "float" ; |
5035 | lldb_format = "vector-float32" ; |
5036 | break; |
5037 | case VectorOfUInt128: |
5038 | gdb_type = "float" ; |
5039 | lldb_format = "vector-uint128" ; |
5040 | break; |
5041 | }; |
5042 | |
5043 | uint32_t indent = 2; |
5044 | |
5045 | XMLElementStart(s, indent, name: "reg" , has_attributes: true); |
5046 | XMLAttributeString(s, name: "name" , value: reg.nub_info.name); |
5047 | XMLAttributeUnsignedDecimal(s, name: "regnum" , value: reg_num); |
5048 | XMLAttributeUnsignedDecimal(s, name: "offset" , value: reg.offset); |
5049 | XMLAttributeUnsignedDecimal(s, name: "bitsize" , value: reg.nub_info.size * 8); |
5050 | XMLAttributeString(s, name: "group" , value: gdb_group); |
5051 | XMLAttributeString(s, name: "type" , value: gdb_type, default_value: default_gdb_type); |
5052 | XMLAttributeString(s, name: "altname" , value: reg.nub_info.alt); |
5053 | XMLAttributeString(s, name: "encoding" , value: lldb_encoding, default_value: default_lldb_encoding); |
5054 | XMLAttributeString(s, name: "format" , value: lldb_format, default_value: default_lldb_format); |
5055 | XMLAttributeUnsignedDecimal(s, name: "group_id" , value: reg.nub_info.set); |
5056 | if (reg.nub_info.reg_ehframe != INVALID_NUB_REGNUM) |
5057 | XMLAttributeUnsignedDecimal(s, name: "ehframe_regnum" , value: reg.nub_info.reg_ehframe); |
5058 | if (reg.nub_info.reg_dwarf != INVALID_NUB_REGNUM) |
5059 | XMLAttributeUnsignedDecimal(s, name: "dwarf_regnum" , value: reg.nub_info.reg_dwarf); |
5060 | |
5061 | const char *lldb_generic = NULL; |
5062 | switch (reg.nub_info.reg_generic) { |
5063 | case GENERIC_REGNUM_FP: |
5064 | lldb_generic = "fp" ; |
5065 | break; |
5066 | case GENERIC_REGNUM_PC: |
5067 | lldb_generic = "pc" ; |
5068 | break; |
5069 | case GENERIC_REGNUM_SP: |
5070 | lldb_generic = "sp" ; |
5071 | break; |
5072 | case GENERIC_REGNUM_RA: |
5073 | lldb_generic = "ra" ; |
5074 | break; |
5075 | case GENERIC_REGNUM_FLAGS: |
5076 | lldb_generic = "flags" ; |
5077 | break; |
5078 | case GENERIC_REGNUM_ARG1: |
5079 | lldb_generic = "arg1" ; |
5080 | break; |
5081 | case GENERIC_REGNUM_ARG2: |
5082 | lldb_generic = "arg2" ; |
5083 | break; |
5084 | case GENERIC_REGNUM_ARG3: |
5085 | lldb_generic = "arg3" ; |
5086 | break; |
5087 | case GENERIC_REGNUM_ARG4: |
5088 | lldb_generic = "arg4" ; |
5089 | break; |
5090 | case GENERIC_REGNUM_ARG5: |
5091 | lldb_generic = "arg5" ; |
5092 | break; |
5093 | case GENERIC_REGNUM_ARG6: |
5094 | lldb_generic = "arg6" ; |
5095 | break; |
5096 | case GENERIC_REGNUM_ARG7: |
5097 | lldb_generic = "arg7" ; |
5098 | break; |
5099 | case GENERIC_REGNUM_ARG8: |
5100 | lldb_generic = "arg8" ; |
5101 | break; |
5102 | default: |
5103 | break; |
5104 | } |
5105 | XMLAttributeString(s, name: "generic" , value: lldb_generic); |
5106 | |
5107 | bool empty = reg.value_regnums.empty() && reg.invalidate_regnums.empty(); |
5108 | if (!empty) { |
5109 | if (!reg.value_regnums.empty()) { |
5110 | std::ostringstream regnums; |
5111 | bool first = true; |
5112 | regnums << DECIMAL; |
5113 | for (auto regnum : reg.value_regnums) { |
5114 | if (!first) |
5115 | regnums << ','; |
5116 | regnums << regnum; |
5117 | first = false; |
5118 | } |
5119 | XMLAttributeString(s, name: "value_regnums" , value: regnums.str().c_str()); |
5120 | } |
5121 | |
5122 | if (!reg.invalidate_regnums.empty()) { |
5123 | std::ostringstream regnums; |
5124 | bool first = true; |
5125 | regnums << DECIMAL; |
5126 | for (auto regnum : reg.invalidate_regnums) { |
5127 | if (!first) |
5128 | regnums << ','; |
5129 | regnums << regnum; |
5130 | first = false; |
5131 | } |
5132 | XMLAttributeString(s, name: "invalidate_regnums" , value: regnums.str().c_str()); |
5133 | } |
5134 | } |
5135 | XMLElementStartEndAttributes(s, empty: true); |
5136 | } |
5137 | |
5138 | void GenerateTargetXMLRegisters(std::ostringstream &s) { |
5139 | nub_size_t num_reg_sets = 0; |
5140 | const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(num_reg_sets: &num_reg_sets); |
5141 | |
5142 | uint32_t cputype = DNBGetRegisterCPUType(); |
5143 | if (cputype) { |
5144 | XMLElementStart(s, indent: 0, name: "feature" , has_attributes: true); |
5145 | std::ostringstream name_strm; |
5146 | name_strm << "com.apple.debugserver." << GetArchName(cputype, cpusubtype: 0); |
5147 | XMLAttributeString(s, name: "name" , value: name_strm.str().c_str()); |
5148 | XMLElementStartEndAttributes(s, empty: false); |
5149 | for (uint32_t reg_num = 0; reg_num < g_num_reg_entries; ++reg_num) |
5150 | // for (const auto ®: g_dynamic_register_map) |
5151 | { |
5152 | GenerateTargetXMLRegister(s, reg_num, num_reg_sets, reg_set_info: reg_sets, |
5153 | reg: g_reg_entries[reg_num]); |
5154 | } |
5155 | XMLElementEnd(s, indent: 0, name: "feature" ); |
5156 | |
5157 | if (num_reg_sets > 0) { |
5158 | XMLElementStart(s, indent: 0, name: "groups" , has_attributes: false); |
5159 | for (uint32_t set = 1; set < num_reg_sets; ++set) { |
5160 | XMLElementStart(s, indent: 2, name: "group" , has_attributes: true); |
5161 | XMLAttributeUnsignedDecimal(s, name: "id" , value: set); |
5162 | XMLAttributeString(s, name: "name" , value: reg_sets[set].name); |
5163 | XMLElementStartEndAttributes(s, empty: true); |
5164 | } |
5165 | XMLElementEnd(s, indent: 0, name: "groups" ); |
5166 | } |
5167 | } |
5168 | } |
5169 | |
5170 | static const char * = R"(<?xml version="1.0"?> |
5171 | <target version="1.0">)" ; |
5172 | |
5173 | static const char * = "</target>" ; |
5174 | |
5175 | static std::string g_target_xml; |
5176 | |
5177 | void UpdateTargetXML() { |
5178 | std::ostringstream s; |
5179 | s << g_target_xml_header << std::endl; |
5180 | |
5181 | // Set the architecture |
5182 | // |
5183 | // On raw targets (no OS, vendor info), I've seen replies like |
5184 | // <architecture>i386:x86-64</architecture> (for x86_64 systems - from vmware) |
5185 | // <architecture>arm</architecture> (for an unspecified arm device - from a Segger JLink) |
5186 | // For good interop, I'm not sure what's expected here. e.g. will anyone understand |
5187 | // <architecture>x86_64</architecture> ? Or is i386:x86_64 the expected phrasing? |
5188 | // |
5189 | // s << "<architecture>" << arch "</architecture>" << std::endl; |
5190 | |
5191 | // Set the OSABI |
5192 | // s << "<osabi>abi-name</osabi>" |
5193 | |
5194 | GenerateTargetXMLRegisters(s); |
5195 | |
5196 | s << g_target_xml_footer << std::endl; |
5197 | |
5198 | // Save the XML output in case it gets retrieved in chunks |
5199 | g_target_xml = s.str(); |
5200 | } |
5201 | |
5202 | rnb_err_t RNBRemote::HandlePacket_qXfer(const char *command) { |
5203 | const char *p = command; |
5204 | p += strlen(s: "qXfer:" ); |
5205 | const char *sep = strchr(s: p, c: ':'); |
5206 | if (sep) { |
5207 | std::string object(p, sep - p); // "auxv", "backtrace", "features", etc |
5208 | p = sep + 1; |
5209 | sep = strchr(s: p, c: ':'); |
5210 | if (sep) { |
5211 | std::string rw(p, sep - p); // "read" or "write" |
5212 | p = sep + 1; |
5213 | sep = strchr(s: p, c: ':'); |
5214 | if (sep) { |
5215 | std::string annex(p, sep - p); // "read" or "write" |
5216 | |
5217 | p = sep + 1; |
5218 | sep = strchr(s: p, c: ','); |
5219 | if (sep) { |
5220 | std::string offset_str(p, sep - p); // read the length as a string |
5221 | p = sep + 1; |
5222 | std::string length_str(p); // read the offset as a string |
5223 | char *end = nullptr; |
5224 | const uint64_t offset = strtoul(nptr: offset_str.c_str(), endptr: &end, |
5225 | base: 16); // convert offset_str to a offset |
5226 | if (*end == '\0') { |
5227 | const uint64_t length = strtoul( |
5228 | nptr: length_str.c_str(), endptr: &end, base: 16); // convert length_str to a length |
5229 | if (*end == '\0') { |
5230 | if (object == "features" && rw == "read" && |
5231 | annex == "target.xml" ) { |
5232 | std::ostringstream xml_out; |
5233 | |
5234 | if (offset == 0) { |
5235 | InitializeRegisters(force: true); |
5236 | |
5237 | UpdateTargetXML(); |
5238 | if (g_target_xml.empty()) |
5239 | return SendErrorPacket(errcode: "E83" ); |
5240 | |
5241 | if (length > g_target_xml.size()) { |
5242 | xml_out << 'l'; // No more data |
5243 | xml_out << binary_encode_string(s: g_target_xml); |
5244 | } else { |
5245 | xml_out << 'm'; // More data needs to be read with a |
5246 | // subsequent call |
5247 | xml_out << binary_encode_string( |
5248 | s: std::string(g_target_xml, offset, length)); |
5249 | } |
5250 | } else { |
5251 | // Retrieving target XML in chunks |
5252 | if (offset < g_target_xml.size()) { |
5253 | std::string chunk(g_target_xml, offset, length); |
5254 | if (chunk.size() < length) |
5255 | xml_out << 'l'; // No more data |
5256 | else |
5257 | xml_out << 'm'; // More data needs to be read with a |
5258 | // subsequent call |
5259 | xml_out << binary_encode_string(s: chunk.data()); |
5260 | } |
5261 | } |
5262 | return SendPacket(s: xml_out.str()); |
5263 | } |
5264 | // Well formed, put not supported |
5265 | return HandlePacket_UNIMPLEMENTED(p: command); |
5266 | } |
5267 | } |
5268 | } |
5269 | } else { |
5270 | SendErrorPacket(errcode: "E85" ); |
5271 | } |
5272 | } else { |
5273 | SendErrorPacket(errcode: "E86" ); |
5274 | } |
5275 | } |
5276 | return SendErrorPacket(errcode: "E82" ); |
5277 | } |
5278 | |
5279 | rnb_err_t RNBRemote::HandlePacket_qGDBServerVersion(const char *p) { |
5280 | std::ostringstream strm; |
5281 | |
5282 | #if defined(DEBUGSERVER_PROGRAM_NAME) |
5283 | strm << "name:" DEBUGSERVER_PROGRAM_NAME ";" ; |
5284 | #else |
5285 | strm << "name:debugserver;" ; |
5286 | #endif |
5287 | strm << "version:" << DEBUGSERVER_VERSION_NUM << ";" ; |
5288 | |
5289 | return SendPacket(s: strm.str()); |
5290 | } |
5291 | |
5292 | rnb_err_t RNBRemote::HandlePacket_jGetDyldProcessState(const char *p) { |
5293 | const nub_process_t pid = m_ctx.ProcessID(); |
5294 | if (pid == INVALID_NUB_PROCESS) |
5295 | return SendErrorPacket(errcode: "E87" ); |
5296 | |
5297 | JSONGenerator::ObjectSP dyld_state_sp = DNBGetDyldProcessState(pid); |
5298 | if (dyld_state_sp) { |
5299 | std::ostringstream strm; |
5300 | dyld_state_sp->DumpBinaryEscaped(strm); |
5301 | dyld_state_sp->Clear(); |
5302 | if (strm.str().size() > 0) |
5303 | return SendPacket(s: strm.str()); |
5304 | } |
5305 | return SendErrorPacket(errcode: "E88" ); |
5306 | } |
5307 | |
5308 | // A helper function that retrieves a single integer value from |
5309 | // a one-level-deep JSON dictionary of key-value pairs. e.g. |
5310 | // jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}] |
5311 | // |
5312 | uint64_t get_integer_value_for_key_name_from_json(const char *key, |
5313 | const char *json_string) { |
5314 | uint64_t retval = INVALID_NUB_ADDRESS; |
5315 | std::string key_with_quotes = "\"" ; |
5316 | key_with_quotes += key; |
5317 | key_with_quotes += "\"" ; |
5318 | const char *c = strstr(haystack: json_string, needle: key_with_quotes.c_str()); |
5319 | if (c) { |
5320 | c += key_with_quotes.size(); |
5321 | |
5322 | while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5323 | c++; |
5324 | |
5325 | if (*c == ':') { |
5326 | c++; |
5327 | |
5328 | while (*c != '\0' && |
5329 | (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5330 | c++; |
5331 | |
5332 | errno = 0; |
5333 | retval = strtoul(nptr: c, NULL, base: 10); |
5334 | if (errno != 0) { |
5335 | retval = INVALID_NUB_ADDRESS; |
5336 | } |
5337 | } |
5338 | } |
5339 | return retval; |
5340 | } |
5341 | |
5342 | // A helper function that retrieves a boolean value from |
5343 | // a one-level-deep JSON dictionary of key-value pairs. e.g. |
5344 | // jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}] |
5345 | |
5346 | // Returns true if it was able to find the key name, and sets the 'value' |
5347 | // argument to the value found. |
5348 | |
5349 | bool get_boolean_value_for_key_name_from_json(const char *key, |
5350 | const char *json_string, |
5351 | bool &value) { |
5352 | std::string key_with_quotes = "\"" ; |
5353 | key_with_quotes += key; |
5354 | key_with_quotes += "\"" ; |
5355 | const char *c = strstr(haystack: json_string, needle: key_with_quotes.c_str()); |
5356 | if (c) { |
5357 | c += key_with_quotes.size(); |
5358 | |
5359 | while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5360 | c++; |
5361 | |
5362 | if (*c == ':') { |
5363 | c++; |
5364 | |
5365 | while (*c != '\0' && |
5366 | (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5367 | c++; |
5368 | |
5369 | if (strncmp(s1: c, s2: "true" , n: 4) == 0) { |
5370 | value = true; |
5371 | return true; |
5372 | } else if (strncmp(s1: c, s2: "false" , n: 5) == 0) { |
5373 | value = false; |
5374 | return true; |
5375 | } |
5376 | } |
5377 | } |
5378 | return false; |
5379 | } |
5380 | |
5381 | // A helper function that reads an array of uint64_t's from |
5382 | // a one-level-deep JSON dictionary of key-value pairs. e.g. |
5383 | // jGetLoadedDynamicLibrariesInfos:{"solib_addrs":[31345823,7768020384,7310483024]}] |
5384 | |
5385 | // Returns true if it was able to find the key name, false if it did not. |
5386 | // "ints" will have all integers found in the array appended to it. |
5387 | |
5388 | bool get_array_of_ints_value_for_key_name_from_json( |
5389 | const char *key, const char *json_string, std::vector<uint64_t> &ints) { |
5390 | std::string key_with_quotes = "\"" ; |
5391 | key_with_quotes += key; |
5392 | key_with_quotes += "\"" ; |
5393 | const char *c = strstr(haystack: json_string, needle: key_with_quotes.c_str()); |
5394 | if (c) { |
5395 | c += key_with_quotes.size(); |
5396 | |
5397 | while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5398 | c++; |
5399 | |
5400 | if (*c == ':') { |
5401 | c++; |
5402 | |
5403 | while (*c != '\0' && |
5404 | (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5405 | c++; |
5406 | |
5407 | if (*c == '[') { |
5408 | c++; |
5409 | while (*c != '\0' && |
5410 | (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5411 | c++; |
5412 | while (true) { |
5413 | if (!isdigit(*c)) { |
5414 | return true; |
5415 | } |
5416 | |
5417 | errno = 0; |
5418 | char *endptr; |
5419 | uint64_t value = strtoul(nptr: c, endptr: &endptr, base: 10); |
5420 | if (errno == 0) { |
5421 | ints.push_back(x: value); |
5422 | } else { |
5423 | break; |
5424 | } |
5425 | if (endptr == c || endptr == nullptr || *endptr == '\0') { |
5426 | break; |
5427 | } |
5428 | c = endptr; |
5429 | |
5430 | while (*c != '\0' && |
5431 | (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5432 | c++; |
5433 | if (*c == ',') |
5434 | c++; |
5435 | while (*c != '\0' && |
5436 | (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')) |
5437 | c++; |
5438 | if (*c == ']') { |
5439 | return true; |
5440 | } |
5441 | } |
5442 | } |
5443 | } |
5444 | } |
5445 | return false; |
5446 | } |
5447 | |
5448 | JSONGenerator::ObjectSP |
5449 | RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only) { |
5450 | JSONGenerator::ArraySP threads_array_sp; |
5451 | if (m_ctx.HasValidProcessID()) { |
5452 | threads_array_sp = std::make_shared<JSONGenerator::Array>(); |
5453 | |
5454 | nub_process_t pid = m_ctx.ProcessID(); |
5455 | |
5456 | nub_size_t numthreads = DNBProcessGetNumThreads(pid); |
5457 | for (nub_size_t i = 0; i < numthreads; ++i) { |
5458 | nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, thread_idx: i); |
5459 | |
5460 | struct DNBThreadStopInfo tid_stop_info; |
5461 | |
5462 | const bool stop_info_valid = |
5463 | DNBThreadGetStopReason(pid, tid, stop_info: &tid_stop_info); |
5464 | |
5465 | // If we are doing stop info only, then we only show threads that have a |
5466 | // valid stop reason |
5467 | if (threads_with_valid_stop_info_only) { |
5468 | if (!stop_info_valid || tid_stop_info.reason == eStopTypeInvalid) |
5469 | continue; |
5470 | } |
5471 | |
5472 | JSONGenerator::DictionarySP thread_dict_sp( |
5473 | new JSONGenerator::Dictionary()); |
5474 | thread_dict_sp->AddIntegerItem("tid" , tid); |
5475 | |
5476 | std::string reason_value("none" ); |
5477 | |
5478 | if (stop_info_valid) { |
5479 | switch (tid_stop_info.reason) { |
5480 | case eStopTypeInvalid: |
5481 | break; |
5482 | |
5483 | case eStopTypeSignal: |
5484 | if (tid_stop_info.details.signal.signo != 0) { |
5485 | thread_dict_sp->AddIntegerItem("signal" , |
5486 | tid_stop_info.details.signal.signo); |
5487 | reason_value = "signal" ; |
5488 | } |
5489 | break; |
5490 | |
5491 | case eStopTypeException: |
5492 | if (tid_stop_info.details.exception.type != 0) { |
5493 | reason_value = "exception" ; |
5494 | thread_dict_sp->AddIntegerItem( |
5495 | "metype" , tid_stop_info.details.exception.type); |
5496 | JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array()); |
5497 | for (nub_size_t i = 0; |
5498 | i < tid_stop_info.details.exception.data_count; ++i) { |
5499 | medata_array_sp->AddItem( |
5500 | JSONGenerator::IntegerSP(new JSONGenerator::Integer( |
5501 | tid_stop_info.details.exception.data[i]))); |
5502 | } |
5503 | thread_dict_sp->AddItem("medata" , medata_array_sp); |
5504 | } |
5505 | break; |
5506 | |
5507 | case eStopTypeWatchpoint: { |
5508 | reason_value = "watchpoint" ; |
5509 | thread_dict_sp->AddIntegerItem("watchpoint" , |
5510 | tid_stop_info.details.watchpoint.addr); |
5511 | thread_dict_sp->AddIntegerItem( |
5512 | "me_watch_addr" , |
5513 | tid_stop_info.details.watchpoint.mach_exception_addr); |
5514 | std::ostringstream wp_desc; |
5515 | wp_desc << tid_stop_info.details.watchpoint.addr << " " ; |
5516 | wp_desc << tid_stop_info.details.watchpoint.hw_idx << " " ; |
5517 | wp_desc << tid_stop_info.details.watchpoint.mach_exception_addr; |
5518 | thread_dict_sp->AddStringItem("description" , wp_desc.str()); |
5519 | } break; |
5520 | |
5521 | case eStopTypeExec: |
5522 | reason_value = "exec" ; |
5523 | break; |
5524 | } |
5525 | } |
5526 | |
5527 | thread_dict_sp->AddStringItem("reason" , reason_value); |
5528 | |
5529 | if (!threads_with_valid_stop_info_only) { |
5530 | const char *thread_name = DNBThreadGetName(pid, tid); |
5531 | if (thread_name && thread_name[0]) |
5532 | thread_dict_sp->AddStringItem("name" , thread_name); |
5533 | |
5534 | thread_identifier_info_data_t thread_ident_info; |
5535 | if (DNBThreadGetIdentifierInfo(pid, tid, &thread_ident_info)) { |
5536 | if (thread_ident_info.dispatch_qaddr != 0) { |
5537 | thread_dict_sp->AddIntegerItem("qaddr" , |
5538 | thread_ident_info.dispatch_qaddr); |
5539 | |
5540 | const DispatchQueueOffsets *dispatch_queue_offsets = |
5541 | GetDispatchQueueOffsets(); |
5542 | if (dispatch_queue_offsets) { |
5543 | std::string queue_name; |
5544 | uint64_t queue_width = 0; |
5545 | uint64_t queue_serialnum = 0; |
5546 | nub_addr_t dispatch_queue_t = INVALID_NUB_ADDRESS; |
5547 | dispatch_queue_offsets->GetThreadQueueInfo( |
5548 | pid, dispatch_qaddr: thread_ident_info.dispatch_qaddr, dispatch_queue_t, |
5549 | queue_name, queue_width, queue_serialnum); |
5550 | if (dispatch_queue_t == 0 && queue_name.empty() && |
5551 | queue_serialnum == 0) { |
5552 | thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue" , |
5553 | false); |
5554 | } else { |
5555 | thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue" , |
5556 | true); |
5557 | } |
5558 | if (dispatch_queue_t != INVALID_NUB_ADDRESS && |
5559 | dispatch_queue_t != 0) |
5560 | thread_dict_sp->AddIntegerItem("dispatch_queue_t" , |
5561 | dispatch_queue_t); |
5562 | if (!queue_name.empty()) |
5563 | thread_dict_sp->AddStringItem("qname" , queue_name); |
5564 | if (queue_width == 1) |
5565 | thread_dict_sp->AddStringItem("qkind" , "serial" ); |
5566 | else if (queue_width > 1) |
5567 | thread_dict_sp->AddStringItem("qkind" , "concurrent" ); |
5568 | if (queue_serialnum > 0) |
5569 | thread_dict_sp->AddIntegerItem("qserialnum" , queue_serialnum); |
5570 | } |
5571 | } |
5572 | } |
5573 | |
5574 | DNBRegisterValue reg_value; |
5575 | |
5576 | if (g_reg_entries != NULL) { |
5577 | JSONGenerator::DictionarySP registers_dict_sp( |
5578 | new JSONGenerator::Dictionary()); |
5579 | |
5580 | for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) { |
5581 | // Expedite all registers in the first register set that aren't |
5582 | // contained in other registers |
5583 | if (g_reg_entries[reg].nub_info.set == 1 && |
5584 | g_reg_entries[reg].nub_info.value_regs == NULL) { |
5585 | if (!DNBThreadGetRegisterValueByID( |
5586 | pid, tid, set: g_reg_entries[reg].nub_info.set, |
5587 | reg: g_reg_entries[reg].nub_info.reg, value: ®_value)) |
5588 | continue; |
5589 | |
5590 | std::ostringstream reg_num; |
5591 | reg_num << std::dec << g_reg_entries[reg].debugserver_regnum; |
5592 | // Encode native byte ordered bytes as hex ascii |
5593 | registers_dict_sp->AddBytesAsHexASCIIString( |
5594 | reg_num.str(), reg_value.value.v_uint8, |
5595 | g_reg_entries[reg].nub_info.size); |
5596 | } |
5597 | } |
5598 | thread_dict_sp->AddItem("registers" , registers_dict_sp); |
5599 | } |
5600 | |
5601 | // Add expedited stack memory so stack backtracing doesn't need to read |
5602 | // anything from the |
5603 | // frame pointer chain. |
5604 | StackMemoryMap stack_mmap; |
5605 | ReadStackMemory(pid, tid, stack_mmap); |
5606 | if (!stack_mmap.empty()) { |
5607 | JSONGenerator::ArraySP memory_array_sp(new JSONGenerator::Array()); |
5608 | |
5609 | for (const auto &stack_memory : stack_mmap) { |
5610 | JSONGenerator::DictionarySP stack_memory_sp( |
5611 | new JSONGenerator::Dictionary()); |
5612 | stack_memory_sp->AddIntegerItem("address" , stack_memory.first); |
5613 | stack_memory_sp->AddBytesAsHexASCIIString( |
5614 | "bytes" , stack_memory.second.bytes, stack_memory.second.length); |
5615 | memory_array_sp->AddItem(stack_memory_sp); |
5616 | } |
5617 | thread_dict_sp->AddItem("memory" , memory_array_sp); |
5618 | } |
5619 | } |
5620 | |
5621 | threads_array_sp->AddItem(thread_dict_sp); |
5622 | } |
5623 | } |
5624 | return threads_array_sp; |
5625 | } |
5626 | |
5627 | rnb_err_t RNBRemote::HandlePacket_jThreadsInfo(const char *p) { |
5628 | JSONGenerator::ObjectSP threads_info_sp; |
5629 | std::ostringstream json; |
5630 | std::ostringstream reply_strm; |
5631 | // If we haven't run the process yet, return an error. |
5632 | if (m_ctx.HasValidProcessID()) { |
5633 | const bool threads_with_valid_stop_info_only = false; |
5634 | JSONGenerator::ObjectSP threads_info_sp = |
5635 | GetJSONThreadsInfo(threads_with_valid_stop_info_only); |
5636 | |
5637 | if (threads_info_sp) { |
5638 | std::ostringstream strm; |
5639 | threads_info_sp->DumpBinaryEscaped(strm); |
5640 | threads_info_sp->Clear(); |
5641 | if (strm.str().size() > 0) |
5642 | return SendPacket(s: strm.str()); |
5643 | } |
5644 | } |
5645 | return SendErrorPacket(errcode: "E85" ); |
5646 | } |
5647 | |
5648 | rnb_err_t RNBRemote::HandlePacket_jThreadExtendedInfo(const char *p) { |
5649 | nub_process_t pid; |
5650 | std::ostringstream json; |
5651 | // If we haven't run the process yet, return an error. |
5652 | if (!m_ctx.HasValidProcessID()) { |
5653 | return SendErrorPacket(errcode: "E81" ); |
5654 | } |
5655 | |
5656 | pid = m_ctx.ProcessID(); |
5657 | |
5658 | const char thread_extended_info_str[] = {"jThreadExtendedInfo:{" }; |
5659 | if (strncmp(s1: p, s2: thread_extended_info_str, |
5660 | n: sizeof(thread_extended_info_str) - 1) == 0) { |
5661 | p += strlen(s: thread_extended_info_str); |
5662 | |
5663 | uint64_t tid = get_integer_value_for_key_name_from_json(key: "thread" , json_string: p); |
5664 | uint64_t plo_pthread_tsd_base_address_offset = |
5665 | get_integer_value_for_key_name_from_json( |
5666 | key: "plo_pthread_tsd_base_address_offset" , json_string: p); |
5667 | uint64_t plo_pthread_tsd_base_offset = |
5668 | get_integer_value_for_key_name_from_json(key: "plo_pthread_tsd_base_offset" , |
5669 | json_string: p); |
5670 | uint64_t plo_pthread_tsd_entry_size = |
5671 | get_integer_value_for_key_name_from_json(key: "plo_pthread_tsd_entry_size" , |
5672 | json_string: p); |
5673 | uint64_t dti_qos_class_index = |
5674 | get_integer_value_for_key_name_from_json(key: "dti_qos_class_index" , json_string: p); |
5675 | |
5676 | if (tid != INVALID_NUB_ADDRESS) { |
5677 | nub_addr_t pthread_t_value = DNBGetPThreadT(pid, tid); |
5678 | |
5679 | uint64_t tsd_address = INVALID_NUB_ADDRESS; |
5680 | if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS && |
5681 | plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS && |
5682 | plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS) { |
5683 | tsd_address = DNBGetTSDAddressForThread( |
5684 | pid, tid, plo_pthread_tsd_base_address_offset, |
5685 | plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); |
5686 | } |
5687 | |
5688 | bool timed_out = false; |
5689 | Genealogy::ThreadActivitySP thread_activity_sp; |
5690 | |
5691 | // If the pthread_t value is invalid, or if we were able to fetch the |
5692 | // thread's TSD base |
5693 | // and got an invalid value back, then we have a thread in early startup |
5694 | // or shutdown and |
5695 | // it's possible that gathering the genealogy information for this thread |
5696 | // go badly. |
5697 | // Ideally fetching this info for a thread in these odd states shouldn't |
5698 | // matter - but |
5699 | // we've seen some problems with these new SPI and threads in edge-casey |
5700 | // states. |
5701 | |
5702 | double genealogy_fetch_time = 0; |
5703 | if (pthread_t_value != INVALID_NUB_ADDRESS && |
5704 | tsd_address != INVALID_NUB_ADDRESS) { |
5705 | DNBTimer timer(false); |
5706 | thread_activity_sp = DNBGetGenealogyInfoForThread(pid, tid, timed_out); |
5707 | genealogy_fetch_time = timer.ElapsedMicroSeconds(update: false) / 1000000.0; |
5708 | } |
5709 | |
5710 | std::unordered_set<uint32_t> |
5711 | process_info_indexes; // an array of the process info #'s seen |
5712 | |
5713 | json << "{" ; |
5714 | |
5715 | bool need_to_print_comma = false; |
5716 | |
5717 | if (thread_activity_sp && !timed_out) { |
5718 | const Genealogy::Activity *activity = |
5719 | &thread_activity_sp->current_activity; |
5720 | bool need_vouchers_comma_sep = false; |
5721 | json << "\"activity_query_timed_out\":false," ; |
5722 | if (genealogy_fetch_time != 0) { |
5723 | // If we append the floating point value with << we'll get it in |
5724 | // scientific |
5725 | // notation. |
5726 | char floating_point_ascii_buffer[64]; |
5727 | floating_point_ascii_buffer[0] = '\0'; |
5728 | snprintf(s: floating_point_ascii_buffer, |
5729 | maxlen: sizeof(floating_point_ascii_buffer), format: "%f" , |
5730 | genealogy_fetch_time); |
5731 | if (strlen(s: floating_point_ascii_buffer) > 0) { |
5732 | if (need_to_print_comma) |
5733 | json << "," ; |
5734 | need_to_print_comma = true; |
5735 | json << "\"activity_query_duration\":" |
5736 | << floating_point_ascii_buffer; |
5737 | } |
5738 | } |
5739 | if (activity->activity_id != 0) { |
5740 | if (need_to_print_comma) |
5741 | json << "," ; |
5742 | need_to_print_comma = true; |
5743 | need_vouchers_comma_sep = true; |
5744 | json << "\"activity\":{" ; |
5745 | json << "\"start\":" << activity->activity_start << "," ; |
5746 | json << "\"id\":" << activity->activity_id << "," ; |
5747 | json << "\"parent_id\":" << activity->parent_id << "," ; |
5748 | json << "\"name\":\"" |
5749 | << json_string_quote_metachars(s: activity->activity_name) << "\"," ; |
5750 | json << "\"reason\":\"" |
5751 | << json_string_quote_metachars(s: activity->reason) << "\"" ; |
5752 | json << "}" ; |
5753 | } |
5754 | if (thread_activity_sp->messages.size() > 0) { |
5755 | need_to_print_comma = true; |
5756 | if (need_vouchers_comma_sep) |
5757 | json << "," ; |
5758 | need_vouchers_comma_sep = true; |
5759 | json << "\"trace_messages\":[" ; |
5760 | bool printed_one_message = false; |
5761 | for (auto iter = thread_activity_sp->messages.begin(); |
5762 | iter != thread_activity_sp->messages.end(); ++iter) { |
5763 | if (printed_one_message) |
5764 | json << "," ; |
5765 | else |
5766 | printed_one_message = true; |
5767 | json << "{" ; |
5768 | json << "\"timestamp\":" << iter->timestamp << "," ; |
5769 | json << "\"activity_id\":" << iter->activity_id << "," ; |
5770 | json << "\"trace_id\":" << iter->trace_id << "," ; |
5771 | json << "\"thread\":" << iter->thread << "," ; |
5772 | json << "\"type\":" << (int)iter->type << "," ; |
5773 | json << "\"process_info_index\":" << iter->process_info_index |
5774 | << "," ; |
5775 | process_info_indexes.insert(iter->process_info_index); |
5776 | json << "\"message\":\"" |
5777 | << json_string_quote_metachars(iter->message) << "\"" ; |
5778 | json << "}" ; |
5779 | } |
5780 | json << "]" ; |
5781 | } |
5782 | if (thread_activity_sp->breadcrumbs.size() == 1) { |
5783 | need_to_print_comma = true; |
5784 | if (need_vouchers_comma_sep) |
5785 | json << "," ; |
5786 | need_vouchers_comma_sep = true; |
5787 | json << "\"breadcrumb\":{" ; |
5788 | for (auto iter = thread_activity_sp->breadcrumbs.begin(); |
5789 | iter != thread_activity_sp->breadcrumbs.end(); ++iter) { |
5790 | json << "\"breadcrumb_id\":" << iter->breadcrumb_id << "," ; |
5791 | json << "\"activity_id\":" << iter->activity_id << "," ; |
5792 | json << "\"timestamp\":" << iter->timestamp << "," ; |
5793 | json << "\"name\":\"" << json_string_quote_metachars(iter->name) |
5794 | << "\"" ; |
5795 | } |
5796 | json << "}" ; |
5797 | } |
5798 | if (process_info_indexes.size() > 0) { |
5799 | need_to_print_comma = true; |
5800 | if (need_vouchers_comma_sep) |
5801 | json << "," ; |
5802 | need_vouchers_comma_sep = true; |
5803 | bool printed_one_process_info = false; |
5804 | for (auto iter = process_info_indexes.begin(); |
5805 | iter != process_info_indexes.end(); ++iter) { |
5806 | if (printed_one_process_info) |
5807 | json << "," ; |
5808 | Genealogy::ProcessExecutableInfoSP image_info_sp; |
5809 | uint32_t idx = *iter; |
5810 | image_info_sp = DNBGetGenealogyImageInfo(pid, idx); |
5811 | if (image_info_sp) { |
5812 | if (!printed_one_process_info) { |
5813 | json << "\"process_infos\":[" ; |
5814 | printed_one_process_info = true; |
5815 | } |
5816 | |
5817 | json << "{" ; |
5818 | char uuid_buf[37]; |
5819 | uuid_unparse_upper(image_info_sp->image_uuid, uuid_buf); |
5820 | json << "\"process_info_index\":" << idx << "," ; |
5821 | json << "\"image_path\":\"" |
5822 | << json_string_quote_metachars(image_info_sp->image_path) |
5823 | << "\"," ; |
5824 | json << "\"image_uuid\":\"" << uuid_buf << "\"" ; |
5825 | json << "}" ; |
5826 | } |
5827 | } |
5828 | if (printed_one_process_info) |
5829 | json << "]" ; |
5830 | } |
5831 | } else { |
5832 | if (timed_out) { |
5833 | if (need_to_print_comma) |
5834 | json << "," ; |
5835 | need_to_print_comma = true; |
5836 | json << "\"activity_query_timed_out\":true" ; |
5837 | if (genealogy_fetch_time != 0) { |
5838 | // If we append the floating point value with << we'll get it in |
5839 | // scientific |
5840 | // notation. |
5841 | char floating_point_ascii_buffer[64]; |
5842 | floating_point_ascii_buffer[0] = '\0'; |
5843 | snprintf(s: floating_point_ascii_buffer, |
5844 | maxlen: sizeof(floating_point_ascii_buffer), format: "%f" , |
5845 | genealogy_fetch_time); |
5846 | if (strlen(s: floating_point_ascii_buffer) > 0) { |
5847 | json << "," ; |
5848 | json << "\"activity_query_duration\":" |
5849 | << floating_point_ascii_buffer; |
5850 | } |
5851 | } |
5852 | } |
5853 | } |
5854 | |
5855 | if (tsd_address != INVALID_NUB_ADDRESS) { |
5856 | if (need_to_print_comma) |
5857 | json << "," ; |
5858 | need_to_print_comma = true; |
5859 | json << "\"tsd_address\":" << tsd_address; |
5860 | |
5861 | if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX) { |
5862 | ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread( |
5863 | pid, tid, tsd: tsd_address, dti_qos_class_index); |
5864 | if (requested_qos.IsValid()) { |
5865 | if (need_to_print_comma) |
5866 | json << "," ; |
5867 | need_to_print_comma = true; |
5868 | json << "\"requested_qos\":{" ; |
5869 | json << "\"enum_value\":" << requested_qos.enum_value << "," ; |
5870 | json << "\"constant_name\":\"" |
5871 | << json_string_quote_metachars(s: requested_qos.constant_name) |
5872 | << "\"," ; |
5873 | json << "\"printable_name\":\"" |
5874 | << json_string_quote_metachars(s: requested_qos.printable_name) |
5875 | << "\"" ; |
5876 | json << "}" ; |
5877 | } |
5878 | } |
5879 | } |
5880 | |
5881 | if (pthread_t_value != INVALID_NUB_ADDRESS) { |
5882 | if (need_to_print_comma) |
5883 | json << "," ; |
5884 | need_to_print_comma = true; |
5885 | json << "\"pthread_t\":" << pthread_t_value; |
5886 | } |
5887 | |
5888 | nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT(pid, tid); |
5889 | if (dispatch_queue_t_value != INVALID_NUB_ADDRESS) { |
5890 | if (need_to_print_comma) |
5891 | json << "," ; |
5892 | need_to_print_comma = true; |
5893 | json << "\"dispatch_queue_t\":" << dispatch_queue_t_value; |
5894 | } |
5895 | |
5896 | json << "}" ; |
5897 | std::string json_quoted = binary_encode_string(s: json.str()); |
5898 | return SendPacket(s: json_quoted); |
5899 | } |
5900 | } |
5901 | return SendPacket(s: "OK" ); |
5902 | } |
5903 | |
5904 | // This packet may be called in one of two ways: |
5905 | // |
5906 | // jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true} |
5907 | // Use the new dyld SPI to get a list of all the libraries loaded. |
5908 | // If "report_load_commands":false" is present, only the dyld SPI |
5909 | // provided information (load address, filepath) is returned. |
5910 | // lldb can ask for the mach-o header/load command details in a |
5911 | // separate packet. |
5912 | // |
5913 | // jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]} |
5914 | // Use the dyld SPI and Mach-O parsing in memory to get the information |
5915 | // about the libraries loaded at these addresses. |
5916 | // |
5917 | rnb_err_t |
5918 | RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos(const char *p) { |
5919 | nub_process_t pid; |
5920 | // If we haven't run the process yet, return an error. |
5921 | if (!m_ctx.HasValidProcessID()) { |
5922 | return SendErrorPacket(errcode: "E83" ); |
5923 | } |
5924 | |
5925 | pid = m_ctx.ProcessID(); |
5926 | |
5927 | const char get_loaded_dynamic_libraries_infos_str[] = { |
5928 | "jGetLoadedDynamicLibrariesInfos:{" }; |
5929 | if (strncmp(s1: p, s2: get_loaded_dynamic_libraries_infos_str, |
5930 | n: sizeof(get_loaded_dynamic_libraries_infos_str) - 1) == 0) { |
5931 | p += strlen(s: get_loaded_dynamic_libraries_infos_str); |
5932 | |
5933 | JSONGenerator::ObjectSP json_sp; |
5934 | |
5935 | std::vector<uint64_t> macho_addresses; |
5936 | bool fetch_all_solibs = false; |
5937 | bool report_load_commands = true; |
5938 | get_boolean_value_for_key_name_from_json(key: "report_load_commands" , json_string: p, |
5939 | value&: report_load_commands); |
5940 | |
5941 | if (get_boolean_value_for_key_name_from_json(key: "fetch_all_solibs" , json_string: p, |
5942 | value&: fetch_all_solibs) && |
5943 | fetch_all_solibs) { |
5944 | json_sp = DNBGetAllLoadedLibrariesInfos(pid, report_load_commands); |
5945 | } else if (get_array_of_ints_value_for_key_name_from_json( |
5946 | key: "solib_addresses" , json_string: p, ints&: macho_addresses)) { |
5947 | json_sp = DNBGetLibrariesInfoForAddresses(pid, macho_addresses); |
5948 | } |
5949 | |
5950 | if (json_sp.get()) { |
5951 | std::ostringstream json_str; |
5952 | json_sp->DumpBinaryEscaped(json_str); |
5953 | json_sp->Clear(); |
5954 | if (json_str.str().size() > 0) { |
5955 | return SendPacket(s: json_str.str()); |
5956 | } else { |
5957 | SendErrorPacket(errcode: "E84" ); |
5958 | } |
5959 | } |
5960 | } |
5961 | return SendPacket(s: "OK" ); |
5962 | } |
5963 | |
5964 | // This packet does not currently take any arguments. So the behavior is |
5965 | // jGetSharedCacheInfo:{} |
5966 | // send information about the inferior's shared cache |
5967 | // jGetSharedCacheInfo: |
5968 | // send "OK" to indicate that this packet is supported |
5969 | rnb_err_t RNBRemote::HandlePacket_jGetSharedCacheInfo(const char *p) { |
5970 | nub_process_t pid; |
5971 | // If we haven't run the process yet, return an error. |
5972 | if (!m_ctx.HasValidProcessID()) { |
5973 | return SendErrorPacket(errcode: "E85" ); |
5974 | } |
5975 | |
5976 | pid = m_ctx.ProcessID(); |
5977 | |
5978 | const char get_shared_cache_info_str[] = {"jGetSharedCacheInfo:{" }; |
5979 | if (strncmp(s1: p, s2: get_shared_cache_info_str, |
5980 | n: sizeof(get_shared_cache_info_str) - 1) == 0) { |
5981 | JSONGenerator::ObjectSP json_sp = DNBGetSharedCacheInfo(pid); |
5982 | |
5983 | if (json_sp.get()) { |
5984 | std::ostringstream json_str; |
5985 | json_sp->DumpBinaryEscaped(json_str); |
5986 | json_sp->Clear(); |
5987 | if (json_str.str().size() > 0) { |
5988 | return SendPacket(s: json_str.str()); |
5989 | } else { |
5990 | SendErrorPacket(errcode: "E86" ); |
5991 | } |
5992 | } |
5993 | } |
5994 | return SendPacket(s: "OK" ); |
5995 | } |
5996 | |
5997 | static bool MachHeaderIsMainExecutable(nub_process_t pid, uint32_t addr_size, |
5998 | nub_addr_t , |
5999 | mach_header &mh) { |
6000 | DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, " |
6001 | "addr_size = %u, mach_header_addr = " |
6002 | "0x%16.16llx)" , |
6003 | pid, addr_size, mach_header_addr); |
6004 | const nub_size_t bytes_read = |
6005 | DNBProcessMemoryRead(pid, mach_header_addr, sizeof(mh), &mh); |
6006 | if (bytes_read == sizeof(mh)) { |
6007 | DNBLogThreadedIf( |
6008 | LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = " |
6009 | "%u, mach_header_addr = 0x%16.16llx): mh = {\n magic = " |
6010 | "0x%8.8x\n cpu = 0x%8.8x\n sub = 0x%8.8x\n filetype = " |
6011 | "%u\n ncmds = %u\n sizeofcmds = 0x%8.8x\n flags = " |
6012 | "0x%8.8x }" , |
6013 | pid, addr_size, mach_header_addr, mh.magic, mh.cputype, mh.cpusubtype, |
6014 | mh.filetype, mh.ncmds, mh.sizeofcmds, mh.flags); |
6015 | if ((addr_size == 4 && mh.magic == MH_MAGIC) || |
6016 | (addr_size == 8 && mh.magic == MH_MAGIC_64)) { |
6017 | if (mh.filetype == MH_EXECUTE) { |
6018 | DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = " |
6019 | "%u, addr_size = %u, mach_header_addr = " |
6020 | "0x%16.16llx) -> this is the " |
6021 | "executable!!!" , |
6022 | pid, addr_size, mach_header_addr); |
6023 | return true; |
6024 | } |
6025 | } |
6026 | } |
6027 | return false; |
6028 | } |
6029 | |
6030 | static nub_addr_t GetMachHeaderForMainExecutable(const nub_process_t pid, |
6031 | const uint32_t addr_size, |
6032 | mach_header &mh) { |
6033 | struct AllImageInfos { |
6034 | uint32_t version; |
6035 | uint32_t dylib_info_count; |
6036 | uint64_t dylib_info_addr; |
6037 | }; |
6038 | |
6039 | uint64_t = 0; |
6040 | |
6041 | const nub_addr_t shlib_addr = DNBProcessGetSharedLibraryInfoAddress(pid); |
6042 | uint8_t bytes[256]; |
6043 | nub_size_t bytes_read = 0; |
6044 | DNBDataRef data(bytes, sizeof(bytes), false); |
6045 | DNBDataRef::offset_t offset = 0; |
6046 | data.SetPointerSize(addr_size); |
6047 | |
6048 | // When we are sitting at __dyld_start, the kernel has placed the |
6049 | // address of the mach header of the main executable on the stack. If we |
6050 | // read the SP and dereference a pointer, we might find the mach header |
6051 | // for the executable. We also just make sure there is only 1 thread |
6052 | // since if we are at __dyld_start we shouldn't have multiple threads. |
6053 | if (DNBProcessGetNumThreads(pid) == 1) { |
6054 | nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, thread_idx: 0); |
6055 | if (tid != INVALID_NUB_THREAD) { |
6056 | DNBRegisterValue sp_value; |
6057 | if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, |
6058 | GENERIC_REGNUM_SP, value: &sp_value)) { |
6059 | uint64_t sp = |
6060 | addr_size == 8 ? sp_value.value.uint64 : sp_value.value.uint32; |
6061 | bytes_read = DNBProcessMemoryRead(pid, addr: sp, size: addr_size, buf: bytes); |
6062 | if (bytes_read == addr_size) { |
6063 | offset = 0; |
6064 | mach_header_addr = data.GetPointer(offset_ptr: &offset); |
6065 | if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh)) |
6066 | return mach_header_addr; |
6067 | } |
6068 | } |
6069 | } |
6070 | } |
6071 | |
6072 | // Check the dyld_all_image_info structure for a list of mach header |
6073 | // since it is a very easy thing to check |
6074 | if (shlib_addr != INVALID_NUB_ADDRESS) { |
6075 | bytes_read = |
6076 | DNBProcessMemoryRead(pid, addr: shlib_addr, size: sizeof(AllImageInfos), buf: bytes); |
6077 | if (bytes_read > 0) { |
6078 | AllImageInfos aii; |
6079 | offset = 0; |
6080 | aii.version = data.Get32(offset_ptr: &offset); |
6081 | aii.dylib_info_count = data.Get32(offset_ptr: &offset); |
6082 | if (aii.dylib_info_count > 0) { |
6083 | aii.dylib_info_addr = data.GetPointer(offset_ptr: &offset); |
6084 | if (aii.dylib_info_addr != 0) { |
6085 | const size_t image_info_byte_size = 3 * addr_size; |
6086 | for (uint32_t i = 0; i < aii.dylib_info_count; ++i) { |
6087 | bytes_read = DNBProcessMemoryRead(pid, addr: aii.dylib_info_addr + |
6088 | i * image_info_byte_size, |
6089 | size: image_info_byte_size, buf: bytes); |
6090 | if (bytes_read != image_info_byte_size) |
6091 | break; |
6092 | offset = 0; |
6093 | mach_header_addr = data.GetPointer(offset_ptr: &offset); |
6094 | if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, |
6095 | mh)) |
6096 | return mach_header_addr; |
6097 | } |
6098 | } |
6099 | } |
6100 | } |
6101 | } |
6102 | |
6103 | // We failed to find the executable's mach header from the all image |
6104 | // infos and by dereferencing the stack pointer. Now we fall back to |
6105 | // enumerating the memory regions and looking for regions that are |
6106 | // executable. |
6107 | DNBRegionInfo region_info; |
6108 | mach_header_addr = 0; |
6109 | while (DNBProcessMemoryRegionInfo(pid, addr: mach_header_addr, region_info: ®ion_info)) { |
6110 | if (region_info.size == 0) |
6111 | break; |
6112 | |
6113 | if (region_info.permissions & eMemoryPermissionsExecutable) { |
6114 | DNBLogThreadedIf( |
6115 | LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx) permissions = %c%c%c: " |
6116 | "checking region for executable mach header" , |
6117 | region_info.addr, region_info.addr + region_info.size, |
6118 | (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', |
6119 | (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', |
6120 | (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-'); |
6121 | if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh)) |
6122 | return mach_header_addr; |
6123 | } else { |
6124 | DNBLogThreadedIf( |
6125 | LOG_RNB_PROC, |
6126 | "[0x%16.16llx - 0x%16.16llx): permissions = %c%c%c: skipping region" , |
6127 | region_info.addr, region_info.addr + region_info.size, |
6128 | (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-', |
6129 | (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-', |
6130 | (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-'); |
6131 | } |
6132 | // Set the address to the next mapped region |
6133 | mach_header_addr = region_info.addr + region_info.size; |
6134 | } |
6135 | bzero(&mh, sizeof(mh)); |
6136 | return INVALID_NUB_ADDRESS; |
6137 | } |
6138 | |
6139 | rnb_err_t RNBRemote::HandlePacket_qSymbol(const char *command) { |
6140 | const char *p = command; |
6141 | p += strlen(s: "qSymbol:" ); |
6142 | const char *sep = strchr(s: p, c: ':'); |
6143 | |
6144 | std::string symbol_name; |
6145 | std::string symbol_value_str; |
6146 | // Extract the symbol value if there is one |
6147 | if (sep > p) |
6148 | symbol_value_str.assign(s: p, n: sep - p); |
6149 | p = sep + 1; |
6150 | |
6151 | if (*p) { |
6152 | // We have a symbol name |
6153 | symbol_name = decode_hex_ascii_string(p); |
6154 | if (!symbol_value_str.empty()) { |
6155 | nub_addr_t symbol_value = decode_uint64(p: symbol_value_str.c_str(), base: 16); |
6156 | if (symbol_name == "dispatch_queue_offsets" ) |
6157 | m_dispatch_queue_offsets_addr = symbol_value; |
6158 | } |
6159 | ++m_qSymbol_index; |
6160 | } else { |
6161 | // No symbol name, set our symbol index to zero so we can |
6162 | // read any symbols that we need |
6163 | m_qSymbol_index = 0; |
6164 | } |
6165 | |
6166 | symbol_name.clear(); |
6167 | |
6168 | if (m_qSymbol_index == 0) { |
6169 | if (m_dispatch_queue_offsets_addr == INVALID_NUB_ADDRESS) |
6170 | symbol_name = "dispatch_queue_offsets" ; |
6171 | else |
6172 | ++m_qSymbol_index; |
6173 | } |
6174 | |
6175 | // // Lookup next symbol when we have one... |
6176 | // if (m_qSymbol_index == 1) |
6177 | // { |
6178 | // } |
6179 | |
6180 | if (symbol_name.empty()) { |
6181 | // Done with symbol lookups |
6182 | return SendPacket(s: "OK" ); |
6183 | } else { |
6184 | std::ostringstream reply; |
6185 | reply << "qSymbol:" ; |
6186 | for (size_t i = 0; i < symbol_name.size(); ++i) |
6187 | reply << RAWHEX8(symbol_name[i]); |
6188 | return SendPacket(s: reply.str()); |
6189 | } |
6190 | } |
6191 | |
6192 | rnb_err_t RNBRemote::HandlePacket_QEnableErrorStrings(const char *p) { |
6193 | m_enable_error_strings = true; |
6194 | return SendPacket(s: "OK" ); |
6195 | } |
6196 | |
6197 | static std::pair<cpu_type_t, cpu_subtype_t> |
6198 | GetCPUTypesFromHost(nub_process_t pid) { |
6199 | cpu_type_t cputype = DNBProcessGetCPUType(pid); |
6200 | if (cputype == 0) { |
6201 | DNBLog("Unable to get the process cpu_type, making a best guess." ); |
6202 | cputype = best_guess_cpu_type(); |
6203 | } |
6204 | |
6205 | bool host_cpu_is_64bit = false; |
6206 | uint32_t is64bit_capable; |
6207 | size_t is64bit_capable_len = sizeof(is64bit_capable); |
6208 | if (sysctlbyname("hw.cpu64bit_capable" , &is64bit_capable, |
6209 | &is64bit_capable_len, NULL, 0) == 0) |
6210 | host_cpu_is_64bit = is64bit_capable != 0; |
6211 | |
6212 | uint32_t cpusubtype; |
6213 | size_t cpusubtype_len = sizeof(cpusubtype); |
6214 | if (::sysctlbyname("hw.cpusubtype" , &cpusubtype, &cpusubtype_len, NULL, 0) == |
6215 | 0) { |
6216 | // If a process is CPU_TYPE_X86, then ignore the cpusubtype that we detected |
6217 | // from the host and use CPU_SUBTYPE_I386_ALL because we don't want the |
6218 | // CPU_SUBTYPE_X86_ARCH1 or CPU_SUBTYPE_X86_64_H to be used as the cpu |
6219 | // subtype |
6220 | // for i386... |
6221 | if (host_cpu_is_64bit) { |
6222 | if (cputype == CPU_TYPE_X86) { |
6223 | cpusubtype = 3; // CPU_SUBTYPE_I386_ALL |
6224 | } else if (cputype == CPU_TYPE_ARM) { |
6225 | // We can query a process' cputype but we cannot query a process' |
6226 | // cpusubtype. |
6227 | // If the process has cputype CPU_TYPE_ARM, then it is an armv7 (32-bit |
6228 | // process) and we |
6229 | // need to override the host cpusubtype (which is in the |
6230 | // CPU_SUBTYPE_ARM64 subtype namespace) |
6231 | // with a reasonable CPU_SUBTYPE_ARMV7 subtype. |
6232 | cpusubtype = 12; // CPU_SUBTYPE_ARM_V7K |
6233 | } |
6234 | } |
6235 | #if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
6236 | // on arm64_32 devices, the machine's native cpu type is |
6237 | // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e. |
6238 | // But we change the cputype to CPU_TYPE_ARM64_32 because |
6239 | // the user processes are all ILP32 processes today. |
6240 | // We also need to rewrite the cpusubtype so we vend |
6241 | // a valid cputype + cpusubtype combination. |
6242 | if (cputype == CPU_TYPE_ARM64_32 && cpusubtype == 2) |
6243 | cpusubtype = CPU_SUBTYPE_ARM64_32_V8; |
6244 | #endif |
6245 | } |
6246 | |
6247 | return {cputype, cpusubtype}; |
6248 | } |
6249 | |
6250 | // Note that all numeric values returned by qProcessInfo are hex encoded, |
6251 | // including the pid and the cpu type. |
6252 | |
6253 | rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) { |
6254 | nub_process_t pid; |
6255 | std::ostringstream rep; |
6256 | |
6257 | // If we haven't run the process yet, return an error. |
6258 | if (!m_ctx.HasValidProcessID()) |
6259 | return SendPacket(s: "E68" ); |
6260 | |
6261 | pid = m_ctx.ProcessID(); |
6262 | |
6263 | rep << "pid:" << std::hex << pid << ';'; |
6264 | |
6265 | int procpid_mib[4]; |
6266 | procpid_mib[0] = CTL_KERN; |
6267 | procpid_mib[1] = KERN_PROC; |
6268 | procpid_mib[2] = KERN_PROC_PID; |
6269 | procpid_mib[3] = pid; |
6270 | struct kinfo_proc proc_kinfo; |
6271 | size_t proc_kinfo_size = sizeof(struct kinfo_proc); |
6272 | |
6273 | if (::sysctl(procpid_mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) { |
6274 | if (proc_kinfo_size > 0) { |
6275 | rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ';'; |
6276 | rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid |
6277 | << ';'; |
6278 | rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid |
6279 | << ';'; |
6280 | rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid |
6281 | << ';'; |
6282 | if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) |
6283 | rep << "effective-gid:" << std::hex |
6284 | << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ';'; |
6285 | } |
6286 | } |
6287 | |
6288 | cpu_type_t cputype; |
6289 | cpu_subtype_t cpusubtype; |
6290 | if (auto cputypes = DNBGetMainBinaryCPUTypes(pid)) |
6291 | std::tie(cputype, cpusubtype) = *cputypes; |
6292 | else |
6293 | std::tie(cputype, cpusubtype) = GetCPUTypesFromHost(pid); |
6294 | |
6295 | uint32_t addr_size = 0; |
6296 | if (cputype != 0) { |
6297 | rep << "cputype:" << std::hex << cputype << ";" ; |
6298 | rep << "cpusubtype:" << std::hex << cpusubtype << ';'; |
6299 | if (cputype & CPU_ARCH_ABI64) |
6300 | addr_size = 8; |
6301 | else |
6302 | addr_size = 4; |
6303 | } |
6304 | |
6305 | bool os_handled = false; |
6306 | if (addr_size > 0) { |
6307 | rep << "ptrsize:" << std::dec << addr_size << ';'; |
6308 | #if defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1 |
6309 | // Try and get the OS type by looking at the load commands in the main |
6310 | // executable and looking for a LC_VERSION_MIN load command. This is the |
6311 | // most reliable way to determine the "ostype" value when on desktop. |
6312 | |
6313 | mach_header mh; |
6314 | nub_addr_t exe_mach_header_addr = |
6315 | GetMachHeaderForMainExecutable(pid, addr_size, mh); |
6316 | if (exe_mach_header_addr != INVALID_NUB_ADDRESS) { |
6317 | uint64_t load_command_addr = |
6318 | exe_mach_header_addr + |
6319 | ((addr_size == 8) ? sizeof(mach_header_64) : sizeof(mach_header)); |
6320 | load_command lc; |
6321 | for (uint32_t i = 0; i < mh.ncmds && !os_handled; ++i) { |
6322 | const nub_size_t bytes_read = |
6323 | DNBProcessMemoryRead(pid, load_command_addr, sizeof(lc), &lc); |
6324 | (void)bytes_read; |
6325 | |
6326 | bool is_executable = true; |
6327 | uint32_t major_version, minor_version, patch_version; |
6328 | std::optional<std::string> platform = |
6329 | DNBGetDeploymentInfo(pid, is_executable, lc, load_command_addr, |
6330 | major_version, minor_version, patch_version); |
6331 | if (platform) { |
6332 | os_handled = true; |
6333 | rep << "ostype:" << *platform << ";" ; |
6334 | break; |
6335 | } |
6336 | load_command_addr = load_command_addr + lc.cmdsize; |
6337 | } |
6338 | } |
6339 | #endif // TARGET_OS_OSX |
6340 | } |
6341 | |
6342 | // If we weren't able to find the OS in a LC_VERSION_MIN load command, try |
6343 | // to set it correctly by using the cpu type and other tricks |
6344 | if (!os_handled) { |
6345 | // The OS in the triple should be "ios" or "macosx" which doesn't match our |
6346 | // "Darwin" which gets returned from "kern.ostype", so we need to hardcode |
6347 | // this for now. |
6348 | if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64 |
6349 | || cputype == CPU_TYPE_ARM64_32) { |
6350 | #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 |
6351 | rep << "ostype:tvos;" ; |
6352 | #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
6353 | rep << "ostype:watchos;" ; |
6354 | #elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 |
6355 | rep << "ostype:bridgeos;" ; |
6356 | #elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1 |
6357 | rep << "ostype:macosx;" ; |
6358 | #else |
6359 | rep << "ostype:ios;" ; |
6360 | #endif |
6361 | } else { |
6362 | bool is_ios_simulator = false; |
6363 | if (cputype == CPU_TYPE_X86 || cputype == CPU_TYPE_X86_64) { |
6364 | // Check for iOS simulator binaries by getting the process argument |
6365 | // and environment and checking for SIMULATOR_UDID in the environment |
6366 | int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2, (int)pid}; |
6367 | |
6368 | uint8_t arg_data[8192]; |
6369 | size_t arg_data_size = sizeof(arg_data); |
6370 | if (::sysctl(proc_args_mib, 3, arg_data, &arg_data_size, NULL, 0) == |
6371 | 0) { |
6372 | DNBDataRef data(arg_data, arg_data_size, false); |
6373 | DNBDataRef::offset_t offset = 0; |
6374 | uint32_t argc = data.Get32(offset_ptr: &offset); |
6375 | const char *cstr; |
6376 | |
6377 | cstr = data.GetCStr(offset_ptr: &offset); |
6378 | if (cstr) { |
6379 | // Skip NULLs |
6380 | while (true) { |
6381 | const char *p = data.PeekCStr(offset); |
6382 | if ((p == NULL) || (*p != '\0')) |
6383 | break; |
6384 | ++offset; |
6385 | } |
6386 | // Now skip all arguments |
6387 | for (uint32_t i = 0; i < argc; ++i) { |
6388 | data.GetCStr(offset_ptr: &offset); |
6389 | } |
6390 | |
6391 | // Now iterate across all environment variables |
6392 | while ((cstr = data.GetCStr(offset_ptr: &offset))) { |
6393 | if (strncmp(s1: cstr, s2: "SIMULATOR_UDID=" , n: strlen(s: "SIMULATOR_UDID=" )) == |
6394 | 0) { |
6395 | is_ios_simulator = true; |
6396 | break; |
6397 | } |
6398 | if (cstr[0] == '\0') |
6399 | break; |
6400 | } |
6401 | } |
6402 | } |
6403 | } |
6404 | if (is_ios_simulator) { |
6405 | #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 |
6406 | rep << "ostype:tvos;" ; |
6407 | #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
6408 | rep << "ostype:watchos;" ; |
6409 | #elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 |
6410 | rep << "ostype:bridgeos;" ; |
6411 | #else |
6412 | rep << "ostype:ios;" ; |
6413 | #endif |
6414 | } else { |
6415 | rep << "ostype:macosx;" ; |
6416 | } |
6417 | } |
6418 | } |
6419 | |
6420 | rep << "vendor:apple;" ; |
6421 | |
6422 | #if defined(__LITTLE_ENDIAN__) |
6423 | rep << "endian:little;" ; |
6424 | #elif defined(__BIG_ENDIAN__) |
6425 | rep << "endian:big;" ; |
6426 | #elif defined(__PDP_ENDIAN__) |
6427 | rep << "endian:pdp;" ; |
6428 | #endif |
6429 | |
6430 | if (addr_size == 0) { |
6431 | #if (defined(__x86_64__) || defined(__i386__)) && defined(x86_THREAD_STATE) |
6432 | nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid); |
6433 | kern_return_t kr; |
6434 | x86_thread_state_t gp_regs; |
6435 | mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT; |
6436 | kr = thread_get_state(static_cast<thread_act_t>(thread), x86_THREAD_STATE, |
6437 | (thread_state_t)&gp_regs, &gp_count); |
6438 | if (kr == KERN_SUCCESS) { |
6439 | if (gp_regs.tsh.flavor == x86_THREAD_STATE64) |
6440 | rep << "ptrsize:8;" ; |
6441 | else |
6442 | rep << "ptrsize:4;" ; |
6443 | } |
6444 | #elif defined(__arm__) |
6445 | rep << "ptrsize:4;" ; |
6446 | #elif (defined(__arm64__) || defined(__aarch64__)) && \ |
6447 | defined(ARM_UNIFIED_THREAD_STATE) |
6448 | nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid); |
6449 | kern_return_t kr; |
6450 | arm_unified_thread_state_t gp_regs; |
6451 | mach_msg_type_number_t gp_count = ARM_UNIFIED_THREAD_STATE_COUNT; |
6452 | kr = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE, |
6453 | (thread_state_t)&gp_regs, &gp_count); |
6454 | if (kr == KERN_SUCCESS) { |
6455 | if (gp_regs.ash.flavor == ARM_THREAD_STATE64) |
6456 | rep << "ptrsize:8;" ; |
6457 | else |
6458 | rep << "ptrsize:4;" ; |
6459 | } |
6460 | #endif |
6461 | } |
6462 | |
6463 | return SendPacket(s: rep.str()); |
6464 | } |
6465 | |
6466 | const RNBRemote::DispatchQueueOffsets *RNBRemote::GetDispatchQueueOffsets() { |
6467 | if (!m_dispatch_queue_offsets.IsValid() && |
6468 | m_dispatch_queue_offsets_addr != INVALID_NUB_ADDRESS && |
6469 | m_ctx.HasValidProcessID()) { |
6470 | nub_process_t pid = m_ctx.ProcessID(); |
6471 | nub_size_t bytes_read = DNBProcessMemoryRead( |
6472 | pid, addr: m_dispatch_queue_offsets_addr, size: sizeof(m_dispatch_queue_offsets), |
6473 | buf: &m_dispatch_queue_offsets); |
6474 | if (bytes_read != sizeof(m_dispatch_queue_offsets)) |
6475 | m_dispatch_queue_offsets.Clear(); |
6476 | } |
6477 | |
6478 | if (m_dispatch_queue_offsets.IsValid()) |
6479 | return &m_dispatch_queue_offsets; |
6480 | else |
6481 | return nullptr; |
6482 | } |
6483 | |
6484 | void RNBRemote::EnableCompressionNextSendPacket(compression_types type) { |
6485 | m_compression_mode = type; |
6486 | m_enable_compression_next_send_packet = true; |
6487 | } |
6488 | |
6489 | compression_types RNBRemote::GetCompressionType() { |
6490 | // The first packet we send back to the debugger after a QEnableCompression |
6491 | // request |
6492 | // should be uncompressed -- so we can indicate whether the compression was |
6493 | // enabled |
6494 | // or not via OK / Enn returns. After that, all packets sent will be using |
6495 | // the |
6496 | // compression protocol. |
6497 | |
6498 | if (m_enable_compression_next_send_packet) { |
6499 | // One time, we send back "None" as our compression type |
6500 | m_enable_compression_next_send_packet = false; |
6501 | return compression_types::none; |
6502 | } |
6503 | return m_compression_mode; |
6504 | } |
6505 | |