VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCGdbRemoteStub.cpp@ 86705

Last change on this file since 86705 was 86327, checked in by vboxsync, 4 years ago

Debugger: Allow for different I/O providers instead of only TCP

So far TCP was the only option to communicate remotely with the internal debugger, the other option
was to use the console from the GUI directly. This commit reworks basic I/O to allow for different
providers where TCP is just one option. The second one being introduced is an IPC provider using a local
socket or named pipe depending on the platform. This allows for Windows kernel debugging over a pipe
using the KD stub in VirtualBox and WinDbg running on the host (not tested yet).

Furthermore this commit allows multiple stubs to be listening for connections at the same time, so
one can have a GDB stub listening on one TCP port and the native VBox debugger listening on another one
or even using a different I/O provider. Only one session can be active at a time though, because sharing
debugger states is impossible. To configure this the following CFGM keys need to be set for each listener:

"DBGC/<Some unique ID>/Provider" "tcp|ipc"
"DBGC/<Some unique ID>/StubType" "native|gdb|kd"
"DBGC/<Some unique ID>/Address" "<ip>|<local named pipe or socket path>"
"DBGC/<Some unique ID>/Port" "<port>" (for TCP only)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 100.2 KB
Line 
1/* $Id: DBGCGdbRemoteStub.cpp 86327 2020-09-28 16:20:50Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, GDB Remote Stub.
4 */
5
6/*
7 * Copyright (C) 2010-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <VBox/dbg.h>
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/vmapi.h> /* VMR3GetVM() */
25#include <VBox/vmm/hm.h> /* HMR3IsEnabled */
26#include <VBox/vmm/nem.h> /* NEMR3IsEnabled */
27#include <iprt/cdefs.h>
28#include <iprt/err.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31#include <iprt/string.h>
32
33#include <stdlib.h>
34
35#include "DBGCInternal.h"
36
37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
41
42/** Character indicating the start of a packet. */
43#define GDBSTUB_PKT_START '$'
44/** Character indicating the end of a packet (excluding the checksum). */
45#define GDBSTUB_PKT_END '#'
46/** The escape character. */
47#define GDBSTUB_PKT_ESCAPE '{'
48/** The out-of-band interrupt character. */
49#define GDBSTUB_OOB_INTERRUPT 0x03
50
51
52/** Indicate support for the 'qXfer:features:read' packet to support the target description. */
53#define GDBSTUBCTX_FEATURES_F_TGT_DESC RT_BIT(0)
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59
60/**
61 * Trace point type.
62 */
63typedef enum GDBSTUBTPTYPE
64{
65 /** Invalid type, do not use. */
66 GDBSTUBTPTYPE_INVALID = 0,
67 /** An instruction software trace point. */
68 GDBSTUBTPTYPE_EXEC_SW,
69 /** An instruction hardware trace point. */
70 GDBSTUBTPTYPE_EXEC_HW,
71 /** A memory read trace point. */
72 GDBSTUBTPTYPE_MEM_READ,
73 /** A memory write trace point. */
74 GDBSTUBTPTYPE_MEM_WRITE,
75 /** A memory access trace point. */
76 GDBSTUBTPTYPE_MEM_ACCESS,
77 /** 32bit hack. */
78 GDBSTUBTPTYPE_32BIT_HACK = 0x7fffffff
79} GDBSTUBTPTYPE;
80
81
82/**
83 * GDB stub receive state.
84 */
85typedef enum GDBSTUBRECVSTATE
86{
87 /** Invalid state. */
88 GDBSTUBRECVSTATE_INVALID = 0,
89 /** Waiting for the start character. */
90 GDBSTUBRECVSTATE_PACKET_WAIT_FOR_START,
91 /** Reiceiving the packet body up until the END character. */
92 GDBSTUBRECVSTATE_PACKET_RECEIVE_BODY,
93 /** Receiving the checksum. */
94 GDBSTUBRECVSTATE_PACKET_RECEIVE_CHECKSUM,
95 /** Blow up the enum to 32bits for easier alignment of members in structs. */
96 GDBSTUBRECVSTATE_32BIT_HACK = 0x7fffffff
97} GDBSTUBRECVSTATE;
98
99
100/**
101 * GDB target register descriptor.
102 */
103typedef struct GDBREGDESC
104{
105 /** Register name. */
106 const char *pszName;
107 /** DBGF register index. */
108 DBGFREG enmReg;
109 /** Bitsize */
110 uint32_t cBits;
111 /** Type. */
112 const char *pszType;
113 /** Group. */
114 const char *pszGroup;
115} GDBREGDESC;
116/** Pointer to a GDB target register descriptor. */
117typedef GDBREGDESC *PGDBREGDESC;
118/** Pointer to a const GDB target register descriptor. */
119typedef const GDBREGDESC *PCGDBREGDESC;
120
121
122/**
123 * A tracepoint descriptor.
124 */
125typedef struct GDBSTUBTP
126{
127 /** List node for the list of tracepoints. */
128 RTLISTNODE NdTps;
129 /** The breakpoint number from the DBGF API. */
130 uint32_t iBp;
131 /** The tracepoint type for identification. */
132 GDBSTUBTPTYPE enmTpType;
133 /** The tracepoint address for identification. */
134 uint64_t GdbTgtAddr;
135 /** The tracepoint kind for identification. */
136 uint64_t uKind;
137} GDBSTUBTP;
138/** Pointer to a tracepoint. */
139typedef GDBSTUBTP *PGDBSTUBTP;
140
141
142/**
143 * GDB stub context data.
144 */
145typedef struct GDBSTUBCTX
146{
147 /** Internal debugger console data. */
148 DBGC Dbgc;
149 /** The current state when receiving a new packet. */
150 GDBSTUBRECVSTATE enmState;
151 /** Maximum number of bytes the packet buffer can hold. */
152 size_t cbPktBufMax;
153 /** Current offset into the packet buffer. */
154 size_t offPktBuf;
155 /** The size of the packet (minus the start, end characters and the checksum). */
156 size_t cbPkt;
157 /** Pointer to the packet buffer data. */
158 uint8_t *pbPktBuf;
159 /** Number of bytes left for the checksum. */
160 size_t cbChksumRecvLeft;
161 /** Send packet checksum. */
162 uint8_t uChkSumSend;
163 /** Feature flags supported we negotiated with the remote end. */
164 uint32_t fFeatures;
165 /** Pointer to the XML target description. */
166 char *pachTgtXmlDesc;
167 /** Size of the XML target description. */
168 size_t cbTgtXmlDesc;
169 /** Pointer to the selected GDB register set. */
170 PCGDBREGDESC paRegs;
171 /** Number of entries in the register set. */
172 uint32_t cRegs;
173 /** Flag whether the stub is in extended mode. */
174 bool fExtendedMode;
175 /** Flag whether was something was output using the 'O' packet since it was reset last. */
176 bool fOutput;
177 /** List of registered trace points.
178 * GDB removes breakpoints/watchpoints using the parameters they were
179 * registered with while we only use the BP number form DBGF internally.
180 * Means we have to track all registration so we can remove them later on. */
181 RTLISTANCHOR LstTps;
182 /** Flag whether a ThreadInfo query was started. */
183 bool fInThrdInfoQuery;
184 /** Next ID to return in the current ThreadInfo query. */
185 VMCPUID idCpuNextThrdInfoQuery;
186} GDBSTUBCTX;
187/** Pointer to the GDB stub context data. */
188typedef GDBSTUBCTX *PGDBSTUBCTX;
189/** Pointer to const GDB stub context data. */
190typedef const GDBSTUBCTX *PCGDBSTUBCTX;
191/** Pointer to a GDB stub context data pointer. */
192typedef PGDBSTUBCTX *PPGDBSTUBCTX;
193
194
195/**
196 * Specific query packet processor callback.
197 *
198 * @returns Status code.
199 * @param pThis The GDB stub context.
200 * @param pbVal Pointer to the remaining value.
201 * @param cbVal Size of the remaining value in bytes.
202 */
203typedef DECLCALLBACKTYPE(int, FNGDBSTUBQPKTPROC,(PGDBSTUBCTX pThis, const uint8_t *pbVal, size_t cbVal));
204typedef FNGDBSTUBQPKTPROC *PFNGDBSTUBQPKTPROC;
205
206
207/**
208 * 'q' packet processor.
209 */
210typedef struct GDBSTUBQPKTPROC
211{
212 /** Name */
213 const char *pszName;
214 /** Length of name in characters (without \0 terminator). */
215 uint32_t cchName;
216 /** The callback to call for processing the particular query. */
217 PFNGDBSTUBQPKTPROC pfnProc;
218} GDBSTUBQPKTPROC;
219/** Pointer to a 'q' packet processor entry. */
220typedef GDBSTUBQPKTPROC *PGDBSTUBQPKTPROC;
221/** Pointer to a const 'q' packet processor entry. */
222typedef const GDBSTUBQPKTPROC *PCGDBSTUBQPKTPROC;
223
224
225/**
226 * 'v' packet processor.
227 */
228typedef struct GDBSTUBVPKTPROC
229{
230 /** Name */
231 const char *pszName;
232 /** Length of name in characters (without \0 terminator). */
233 uint32_t cchName;
234 /** Replay to a query packet (ends with ?). */
235 const char *pszReplyQ;
236 /** Length of the query reply (without \0 terminator). */
237 uint32_t cchReplyQ;
238 /** The callback to call for processing the particular query. */
239 PFNGDBSTUBQPKTPROC pfnProc;
240} GDBSTUBVPKTPROC;
241/** Pointer to a 'q' packet processor entry. */
242typedef GDBSTUBVPKTPROC *PGDBSTUBVPKTPROC;
243/** Pointer to a const 'q' packet processor entry. */
244typedef const GDBSTUBVPKTPROC *PCGDBSTUBVPKTPROC;
245
246
247/**
248 * Feature callback.
249 *
250 * @returns Status code.
251 * @param pThis The GDB stub context.
252 * @param pbVal Pointer to the value.
253 * @param cbVal Size of the value in bytes.
254 */
255typedef DECLCALLBACKTYPE(int, FNGDBSTUBFEATHND,(PGDBSTUBCTX pThis, const uint8_t *pbVal, size_t cbVal));
256typedef FNGDBSTUBFEATHND *PFNGDBSTUBFEATHND;
257
258
259/**
260 * GDB feature descriptor.
261 */
262typedef struct GDBSTUBFEATDESC
263{
264 /** Feature name */
265 const char *pszName;
266 /** Length of the feature name in characters (without \0 terminator). */
267 uint32_t cchName;
268 /** The callback to call for processing the particular feature. */
269 PFNGDBSTUBFEATHND pfnHandler;
270 /** Flag whether the feature requires a value. */
271 bool fVal;
272} GDBSTUBFEATDESC;
273/** Pointer to a GDB feature descriptor. */
274typedef GDBSTUBFEATDESC *PGDBSTUBFEATDESC;
275/** Pointer to a const GDB feature descriptor. */
276typedef const GDBSTUBFEATDESC *PCGDBSTUBFEATDESC;
277
278
279/*********************************************************************************************************************************
280* Internal Functions *
281*********************************************************************************************************************************/
282
283
284/**
285 * Tries to find a trace point with the given parameters in the list of registered trace points.
286 *
287 * @returns Pointer to the trace point registration record if found or NULL if none was found.
288 * @param pThis The GDB stub context.
289 * @param enmTpType The trace point type.
290 * @param GdbTgtAddr Target address given by GDB.
291 * @param uKind Trace point kind.
292 */
293static PGDBSTUBTP dbgcGdbStubTpFind(PGDBSTUBCTX pThis, GDBSTUBTPTYPE enmTpType, uint64_t GdbTgtAddr, uint64_t uKind)
294{
295 PGDBSTUBTP pTpCur = NULL;
296 RTListForEach(&pThis->LstTps, pTpCur, GDBSTUBTP, NdTps)
297 {
298 if ( pTpCur->enmTpType == enmTpType
299 && pTpCur->GdbTgtAddr == GdbTgtAddr
300 && pTpCur->uKind == uKind)
301 return pTpCur;
302 }
303
304 return NULL;
305}
306
307
308/**
309 * Registers a new trace point.
310 *
311 * @returns VBox status code.
312 * @param pThis The GDB stub context.
313 * @param enmTpType The trace point type.
314 * @param GdbTgtAddr Target address given by GDB.
315 * @param uKind Trace point kind.
316 * @param iBp The internal DBGF breakpoint ID this trace point was registered with.
317 */
318static int dbgcGdbStubTpRegister(PGDBSTUBCTX pThis, GDBSTUBTPTYPE enmTpType, uint64_t GdbTgtAddr, uint64_t uKind, uint32_t iBp)
319{
320 int rc = VERR_ALREADY_EXISTS;
321
322 /* Can't register a tracepoint with the same parameters twice or we can't decide whom to remove later on. */
323 PGDBSTUBTP pTp = dbgcGdbStubTpFind(pThis, enmTpType, GdbTgtAddr, uKind);
324 if (!pTp)
325 {
326 pTp = (PGDBSTUBTP)RTMemAllocZ(sizeof(*pTp));
327 if (pTp)
328 {
329 pTp->enmTpType = enmTpType;
330 pTp->GdbTgtAddr = GdbTgtAddr;
331 pTp->uKind = uKind;
332 pTp->iBp = iBp;
333 RTListAppend(&pThis->LstTps, &pTp->NdTps);
334 rc = VINF_SUCCESS;
335 }
336 else
337 rc = VERR_NO_MEMORY;
338 }
339
340 return rc;
341}
342
343
344/**
345 * Deregisters the given trace point (needs to be unregistered from DBGF by the caller before).
346 *
347 * @returns nothing.
348 * @param pTp The trace point to deregister.
349 */
350static void dbgcGdbStubTpDeregister(PGDBSTUBTP pTp)
351{
352 RTListNodeRemove(&pTp->NdTps);
353 RTMemFree(pTp);
354}
355
356
357/**
358 * Converts a given to the hexadecimal value if valid.
359 *
360 * @returns The hexadecimal value the given character represents 0-9,a-f,A-F or 0xff on error.
361 * @param ch The character to convert.
362 */
363DECLINLINE(uint8_t) dbgcGdbStubCtxChrToHex(char ch)
364{
365 if (ch >= '0' && ch <= '9')
366 return ch - '0';
367 if (ch >= 'A' && ch <= 'F')
368 return ch - 'A' + 0xa;
369 if (ch >= 'a' && ch <= 'f')
370 return ch - 'a' + 0xa;
371
372 return 0xff;
373}
374
375
376/**
377 * Converts a 4bit hex number to the appropriate character.
378 *
379 * @returns Character representing the 4bit hex number.
380 * @param uHex The 4 bit hex number.
381 */
382DECLINLINE(char) dbgcGdbStubCtxHexToChr(uint8_t uHex)
383{
384 if (uHex < 0xa)
385 return '0' + uHex;
386 if (uHex <= 0xf)
387 return 'A' + uHex - 0xa;
388
389 return 'X';
390}
391
392
393/**
394 * Wrapper for the I/O interface write callback.
395 *
396 * @returns Status code.
397 * @param pThis The GDB stub context.
398 * @param pvPkt The packet data to send.
399 * @param cbPkt Size of the packet in bytes.
400 */
401DECLINLINE(int) dbgcGdbStubCtxWrite(PGDBSTUBCTX pThis, const void *pvPkt, size_t cbPkt)
402{
403 return pThis->Dbgc.pIo->pfnWrite(pThis->Dbgc.pIo, pvPkt, cbPkt, NULL /*pcbWritten*/);
404}
405
406
407/**
408 * Starts transmission of a new reply packet.
409 *
410 * @returns Status code.
411 * @param pThis The GDB stub context.
412 */
413static int dbgcGdbStubCtxReplySendBegin(PGDBSTUBCTX pThis)
414{
415 pThis->uChkSumSend = 0;
416
417 uint8_t chPktStart = GDBSTUB_PKT_START;
418 return dbgcGdbStubCtxWrite(pThis, &chPktStart, sizeof(chPktStart));
419}
420
421
422/**
423 * Sends the given data in the reply.
424 *
425 * @returns Status code.
426 * @param pThis The GDB stub context.
427 * @param pvReplyData The reply data to send.
428 * @param cbReplyData Size of the reply data in bytes.
429 */
430static int dbgcGdbStubCtxReplySendData(PGDBSTUBCTX pThis, const void *pvReplyData, size_t cbReplyData)
431{
432 /* Update checksum. */
433 const uint8_t *pbData = (const uint8_t *)pvReplyData;
434 for (uint32_t i = 0; i < cbReplyData; i++)
435 pThis->uChkSumSend += pbData[i];
436
437 return dbgcGdbStubCtxWrite(pThis, pvReplyData, cbReplyData);
438}
439
440
441/**
442 * Finishes transmission of the current reply by sending the packet end character and the checksum.
443 *
444 * @returns Status code.
445 * @param pThis The GDB stub context.
446 */
447static int dbgcGdbStubCtxReplySendEnd(PGDBSTUBCTX pThis)
448{
449 uint8_t achPktEnd[3];
450
451 achPktEnd[0] = GDBSTUB_PKT_END;
452 achPktEnd[1] = dbgcGdbStubCtxHexToChr(pThis->uChkSumSend >> 4);
453 achPktEnd[2] = dbgcGdbStubCtxHexToChr(pThis->uChkSumSend & 0xf);
454
455 return dbgcGdbStubCtxWrite(pThis, &achPktEnd[0], sizeof(achPktEnd));
456}
457
458
459/**
460 * Sends the given reply packet, doing the framing, checksumming, etc. in one call.
461 *
462 * @returns Status code.
463 * @param pThis The GDB stub context.
464 * @param pvReplyPkt The reply packet to send.
465 * @param cbReplyPkt Size of the reply packet in bytes.
466 */
467static int dbgcGdbStubCtxReplySend(PGDBSTUBCTX pThis, const void *pvReplyPkt, size_t cbReplyPkt)
468{
469 int rc = dbgcGdbStubCtxReplySendBegin(pThis);
470 if (RT_SUCCESS(rc))
471 {
472 rc = dbgcGdbStubCtxReplySendData(pThis, pvReplyPkt, cbReplyPkt);
473 if (RT_SUCCESS(rc))
474 rc = dbgcGdbStubCtxReplySendEnd(pThis);
475 }
476
477 return rc;
478}
479
480
481/**
482 * Encodes the given buffer as a hexstring string it into the given destination buffer.
483 *
484 * @returns Status code.
485 * @param pbDst Where store the resulting hex string on success.
486 * @param cbDst Size of the destination buffer in bytes.
487 * @param pvSrc The data to encode.
488 * @param cbSrc Number of bytes to encode.
489 */
490DECLINLINE(int) dbgcGdbStubCtxEncodeBinaryAsHex(uint8_t *pbDst, size_t cbDst, const void *pvSrc, size_t cbSrc)
491{
492 return RTStrPrintHexBytes((char *)pbDst, cbDst, pvSrc, cbSrc, RTSTRPRINTHEXBYTES_F_UPPER);
493}
494
495
496/**
497 * Decodes the given ASCII hexstring as binary data up until the given separator is found or the end of the string is reached.
498 *
499 * @returns Status code.
500 * @param pbBuf The buffer containing the hexstring to convert.
501 * @param cbBuf Size of the buffer in bytes.
502 * @param puVal Where to store the decoded integer.
503 * @param chSep The character to stop conversion at.
504 * @param ppbSep Where to store the pointer in the buffer where the separator was found, optional.
505 */
506static int dbgcGdbStubCtxParseHexStringAsInteger(const uint8_t *pbBuf, size_t cbBuf, uint64_t *puVal, uint8_t chSep, const uint8_t **ppbSep)
507{
508 uint64_t uVal = 0;
509
510 while ( cbBuf
511 && *pbBuf != chSep)
512 {
513 uVal = uVal * 16 + dbgcGdbStubCtxChrToHex(*pbBuf++);
514 cbBuf--;
515 }
516
517 *puVal = uVal;
518
519 if (ppbSep)
520 *ppbSep = pbBuf;
521
522 return VINF_SUCCESS;
523}
524
525
526/**
527 * Decodes the given ASCII hexstring as a byte buffer up until the given separator is found or the end of the string is reached.
528 *
529 * @returns Status code.
530 * @param pbBuf The buffer containing the hexstring to convert.
531 * @param cbBuf Size of the buffer in bytes.
532 * @param pvDst Where to store the decoded data.
533 * @param cbDst Maximum buffer size in bytes.
534 * @param pcbDecoded Where to store the number of consumed bytes from the input.
535 */
536DECLINLINE(int) dbgcGdbStubCtxParseHexStringAsByteBuf(const uint8_t *pbBuf, size_t cbBuf, void *pvDst, size_t cbDst, size_t *pcbDecoded)
537{
538 size_t cbDecode = RT_MIN(cbBuf, cbDst * 2);
539
540 if (pcbDecoded)
541 *pcbDecoded = cbDecode;
542
543 return RTStrConvertHexBytes((const char *)pbBuf, pvDst, cbDecode, 0 /* fFlags*/);
544}
545
546#if 0 /*unused for now*/
547/**
548 * Sends a 'OK' part of a reply packet only (packet start and end needs to be handled separately).
549 *
550 * @returns Status code.
551 * @param pThis The GDB stub context.
552 */
553static int dbgcGdbStubCtxReplySendOkData(PGDBSTUBCTX pThis)
554{
555 char achOk[2] = { 'O', 'K' };
556 return dbgcGdbStubCtxReplySendData(pThis, &achOk[0], sizeof(achOk));
557}
558#endif
559
560
561/**
562 * Sends a 'OK' reply packet.
563 *
564 * @returns Status code.
565 * @param pThis The GDB stub context.
566 */
567static int dbgcGdbStubCtxReplySendOk(PGDBSTUBCTX pThis)
568{
569 char achOk[2] = { 'O', 'K' };
570 return dbgcGdbStubCtxReplySend(pThis, &achOk[0], sizeof(achOk));
571}
572
573#if 0 /*unused for now*/
574/**
575 * Sends a 'E NN' part of a reply packet only (packet start and end needs to be handled separately).
576 *
577 * @returns Status code.
578 * @param pThis The GDB stub context.
579 * @param uErr The error code to send.
580 */
581static int dbgcGdbStubCtxReplySendErrData(PGDBSTUBCTX pThis, uint8_t uErr)
582{
583 char achErr[3] = { 'E', 0, 0 };
584 achErr[1] = dbgcGdbStubCtxHexToChr(uErr >> 4);
585 achErr[2] = dbgcGdbStubCtxHexToChr(uErr & 0xf);
586 return dbgcGdbStubCtxReplySendData(pThis, &achErr[0], sizeof(achErr));
587}
588#endif
589
590/**
591 * Sends a 'E NN' reply packet.
592 *
593 * @returns Status code.
594 * @param pThis The GDB stub context.
595 * @param uErr The error code to send.
596 */
597static int dbgcGdbStubCtxReplySendErr(PGDBSTUBCTX pThis, uint8_t uErr)
598{
599 char achErr[3] = { 'E', 0, 0 };
600 achErr[1] = dbgcGdbStubCtxHexToChr(uErr >> 4);
601 achErr[2] = dbgcGdbStubCtxHexToChr(uErr & 0xf);
602 return dbgcGdbStubCtxReplySend(pThis, &achErr[0], sizeof(achErr));
603}
604
605
606/**
607 * Sends a signal trap (S 05) packet to indicate that the target has stopped.
608 *
609 * @returns Status code.
610 * @param pThis The GDB stub context.
611 */
612static int dbgcGdbStubCtxReplySendSigTrap(PGDBSTUBCTX pThis)
613{
614 char achReply[32];
615 ssize_t cchStr = RTStrPrintf2(&achReply[0], sizeof(achReply), "T05thread:%02x;", pThis->Dbgc.idCpu + 1);
616 return dbgcGdbStubCtxReplySend(pThis, &achReply[0], cchStr);
617}
618
619
620/**
621 * Sends a GDB stub status code indicating an error using the error reply packet.
622 *
623 * @returns Status code.
624 * @param pThis The GDB stub context.
625 * @param rc The status code to send.
626 */
627static int dbgcGdbStubCtxReplySendErrSts(PGDBSTUBCTX pThis, int rc)
628{
629 /** @todo convert error codes maybe. */
630 return dbgcGdbStubCtxReplySendErr(pThis, (-rc) & 0xff);
631}
632
633
634/**
635 * Ensures that there is at least the given amount of bytes of free space left in the packet buffer.
636 *
637 * @returns Status code (error when increasing the buffer failed).
638 * @param pThis The GDB stub context.
639 * @param cbSpace Number of bytes required.
640 */
641static int dbgcGdbStubCtxEnsurePktBufSpace(PGDBSTUBCTX pThis, size_t cbSpace)
642{
643 if (pThis->cbPktBufMax - pThis->offPktBuf >= cbSpace)
644 return VINF_SUCCESS;
645
646 /* Slow path allocate new buffer and copy content over. */
647 int rc = VINF_SUCCESS;
648 size_t cbPktBufMaxNew = pThis->cbPktBufMax + cbSpace;
649 void *pvNew = RTMemRealloc(pThis->pbPktBuf, cbPktBufMaxNew);
650 if (pvNew)
651 {
652 pThis->pbPktBuf = (uint8_t *)pvNew;
653 pThis->cbPktBufMax = cbPktBufMaxNew;
654 }
655 else
656 rc = VERR_NO_MEMORY;
657
658 return rc;
659}
660
661
662/**
663 * Parses the arguments of a 'Z' and 'z' packet.
664 *
665 * @returns Status code.
666 * @param pbArgs Pointer to the start of the first argument.
667 * @param cbArgs Number of argument bytes.
668 * @param penmTpType Where to store the tracepoint type on success.
669 * @param pGdbTgtAddr Where to store the address on success.
670 * @param puKind Where to store the kind argument on success.
671 */
672static int dbgcGdbStubCtxParseTpPktArgs(const uint8_t *pbArgs, size_t cbArgs, GDBSTUBTPTYPE *penmTpType, uint64_t *pGdbTgtAddr, uint64_t *puKind)
673{
674 const uint8_t *pbPktSep = NULL;
675 uint64_t uType = 0;
676
677 int rc = dbgcGdbStubCtxParseHexStringAsInteger(pbArgs, cbArgs, &uType,
678 ',', &pbPktSep);
679 if (RT_SUCCESS(rc))
680 {
681 cbArgs -= (uintptr_t)(pbPktSep - pbArgs) - 1;
682 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, cbArgs, pGdbTgtAddr,
683 ',', &pbPktSep);
684 if (RT_SUCCESS(rc))
685 {
686 cbArgs -= (uintptr_t)(pbPktSep - pbArgs) - 1;
687 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, cbArgs, puKind,
688 GDBSTUB_PKT_END, NULL);
689 if (RT_SUCCESS(rc))
690 {
691 switch (uType)
692 {
693 case 0:
694 *penmTpType = GDBSTUBTPTYPE_EXEC_SW;
695 break;
696 case 1:
697 *penmTpType = GDBSTUBTPTYPE_EXEC_HW;
698 break;
699 case 2:
700 *penmTpType = GDBSTUBTPTYPE_MEM_WRITE;
701 break;
702 case 3:
703 *penmTpType = GDBSTUBTPTYPE_MEM_READ;
704 break;
705 case 4:
706 *penmTpType = GDBSTUBTPTYPE_MEM_ACCESS;
707 break;
708 default:
709 rc = VERR_INVALID_PARAMETER;
710 break;
711 }
712 }
713 }
714 }
715
716 return rc;
717}
718
719
720/**
721 * Processes the 'TStatus' query.
722 *
723 * @returns Status code.
724 * @param pThis The GDB stub context.
725 * @param pbArgs Pointer to the start of the arguments in the packet.
726 * @param cbArgs Size of arguments in bytes.
727 */
728static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryTStatus(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
729{
730 RT_NOREF(pbArgs, cbArgs);
731
732 char achReply[2] = { 'T', '0' };
733 return dbgcGdbStubCtxReplySend(pThis, &achReply[0], sizeof(achReply));
734}
735
736
737/**
738 * @copydoc FNGDBSTUBQPKTPROC
739 */
740static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessFeatXmlRegs(PGDBSTUBCTX pThis, const uint8_t *pbVal, size_t cbVal)
741{
742 /*
743 * xmlRegisters contain a list of supported architectures delimited by ','.
744 * Check that the architecture is in the supported list.
745 */
746 while (cbVal)
747 {
748 /* Find the next delimiter. */
749 size_t cbThisVal = cbVal;
750 const uint8_t *pbDelim = (const uint8_t *)memchr(pbVal, ',', cbVal);
751 if (pbDelim)
752 cbThisVal = pbDelim - pbVal;
753
754 const size_t cchArch64 = sizeof("i386:x86-64") - 1;
755 const size_t cchArch32 = sizeof("i386") - 1;
756 if ( !memcmp(pbVal, "i386:x86-64", RT_MIN(cbVal, cchArch64))
757 || !memcmp(pbVal, "i386", RT_MIN(cbVal, cchArch32)))
758 {
759 /* Set the flag to support the qXfer:features:read packet. */
760 pThis->fFeatures |= GDBSTUBCTX_FEATURES_F_TGT_DESC;
761 break;
762 }
763
764 cbVal -= cbThisVal + (pbDelim ? 1 : 0);
765 pbVal = pbDelim + (pbDelim ? 1 : 0);
766 }
767
768 return VINF_SUCCESS;
769}
770
771
772/**
773 * Features which can be reported by the remote GDB which we might support.
774 *
775 * @note The sorting matters for features which start the same, the longest must come first.
776 */
777static const GDBSTUBFEATDESC g_aGdbFeatures[] =
778{
779#define GDBSTUBFEATDESC_INIT(a_Name, a_pfnHnd, a_fVal) { a_Name, sizeof(a_Name) - 1, a_pfnHnd, a_fVal }
780 GDBSTUBFEATDESC_INIT("xmlRegisters", dbgcGdbStubCtxPktProcessFeatXmlRegs, true),
781#undef GDBSTUBFEATDESC_INIT
782};
783
784
785/**
786 * Calculates the feature length of the next feature pointed to by the given arguments buffer.
787 *
788 * @returns Status code.
789 * @param pbArgs Pointer to the start of the arguments in the packet.
790 * @param cbArgs Size of arguments in bytes.
791 * @param pcbArg Where to store the size of the argument in bytes on success (excluding the delimiter).
792 * @param pfTerminator Whereto store the flag whether the packet terminator (#) was seen as a delimiter.
793 */
794static int dbgcGdbStubCtxQueryPktQueryFeatureLen(const uint8_t *pbArgs, size_t cbArgs, size_t *pcbArg, bool *pfTerminator)
795{
796 const uint8_t *pbArgCur = pbArgs;
797
798 while ( cbArgs
799 && *pbArgCur != ';'
800 && *pbArgCur != GDBSTUB_PKT_END)
801 {
802 cbArgs--;
803 pbArgCur++;
804 }
805
806 if ( !cbArgs
807 && *pbArgCur != ';'
808 && *pbArgCur != GDBSTUB_PKT_END)
809 return VERR_NET_PROTOCOL_ERROR;
810
811 *pcbArg = pbArgCur - pbArgs;
812 *pfTerminator = *pbArgCur == GDBSTUB_PKT_END ? true : false;
813
814 return VINF_SUCCESS;
815}
816
817
818/**
819 * Sends the reply to the 'qSupported' packet.
820 *
821 * @returns Status code.
822 * @param pThis The GDB stub context.
823 */
824static int dbgcGdbStubCtxPktProcessQuerySupportedReply(PGDBSTUBCTX pThis)
825{
826 /** @todo Enhance. */
827 if (pThis->fFeatures & GDBSTUBCTX_FEATURES_F_TGT_DESC)
828 return dbgcGdbStubCtxReplySend(pThis, "qXfer:features:read+;vContSupported+", sizeof("qXfer:features:read+;vContSupported+") - 1);
829
830 return dbgcGdbStubCtxReplySend(pThis, NULL, 0);
831}
832
833
834/**
835 * Processes the 'Supported' query.
836 *
837 * @returns Status code.
838 * @param pThis The GDB stub context.
839 * @param pbArgs Pointer to the start of the arguments in the packet.
840 * @param cbArgs Size of arguments in bytes.
841 */
842static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQuerySupported(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
843{
844 /* Skip the : following the qSupported start. */
845 if ( cbArgs < 1
846 || pbArgs[0] != ':')
847 return VERR_NET_PROTOCOL_ERROR;
848
849 cbArgs--;
850 pbArgs++;
851
852 /*
853 * Each feature but the last one are separated by ; and the last one is delimited by the # packet end symbol.
854 * We first determine the boundaries of the reported feature and pass it to the appropriate handler.
855 */
856 int rc = VINF_SUCCESS;
857 while ( cbArgs
858 && RT_SUCCESS(rc))
859 {
860 bool fTerminator = false;
861 size_t cbArg = 0;
862 rc = dbgcGdbStubCtxQueryPktQueryFeatureLen(pbArgs, cbArgs, &cbArg, &fTerminator);
863 if (RT_SUCCESS(rc))
864 {
865 /* Search for the feature handler. */
866 for (uint32_t i = 0; i < RT_ELEMENTS(g_aGdbFeatures); i++)
867 {
868 PCGDBSTUBFEATDESC pFeatDesc = &g_aGdbFeatures[i];
869
870 if ( cbArg > pFeatDesc->cchName /* At least one character must come after the feature name ('+', '-' or '='). */
871 && !memcmp(pFeatDesc->pszName, pbArgs, pFeatDesc->cchName))
872 {
873 /* Found, execute handler after figuring out whether there is a value attached. */
874 const uint8_t *pbVal = pbArgs + pFeatDesc->cchName;
875 size_t cbVal = cbArg - pFeatDesc->cchName;
876
877 if (pFeatDesc->fVal)
878 {
879 if ( *pbVal == '='
880 && cbVal > 1)
881 {
882 pbVal++;
883 cbVal--;
884 }
885 else
886 rc = VERR_NET_PROTOCOL_ERROR;
887 }
888 else if ( cbVal != 1
889 || ( *pbVal != '+'
890 && *pbVal != '-')) /* '+' and '-' are allowed to indicate support for a particular feature. */
891 rc = VERR_NET_PROTOCOL_ERROR;
892
893 if (RT_SUCCESS(rc))
894 rc = pFeatDesc->pfnHandler(pThis, pbVal, cbVal);
895 break;
896 }
897 }
898
899 cbArgs -= cbArg;
900 pbArgs += cbArg;
901 if (!fTerminator)
902 {
903 cbArgs--;
904 pbArgs++;
905 }
906 else
907 break;
908 }
909 }
910
911 /* If everything went alright send the reply with our supported features. */
912 if (RT_SUCCESS(rc))
913 rc = dbgcGdbStubCtxPktProcessQuerySupportedReply(pThis);
914
915 return rc;
916}
917
918
919/**
920 * Sends the reply to a 'qXfer:object:read:...' request.
921 *
922 * @returns Status code.
923 * @param pThis The GDB stub context.
924 * @param offRead Where to start reading from within the object.
925 * @param cbRead How much to read.
926 * @param pbObj The start of the object.
927 * @param cbObj Size of the object.
928 */
929static int dbgcGdbStubCtxQueryXferReadReply(PGDBSTUBCTX pThis, uint32_t offRead, size_t cbRead, const uint8_t *pbObj, size_t cbObj)
930{
931 int rc = VINF_SUCCESS;
932 if (offRead < cbObj)
933 {
934 /** @todo Escaping */
935 size_t cbThisRead = offRead + cbRead < cbObj ? cbRead : cbObj - offRead;
936
937 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbThisRead + 1);
938 if (RT_SUCCESS(rc))
939 {
940 uint8_t *pbPktBuf = pThis->pbPktBuf;
941 *pbPktBuf++ = cbThisRead < cbRead ? 'l' : 'm';
942 memcpy(pbPktBuf, pbObj + offRead, cbThisRead);
943 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbThisRead + 1);
944 }
945 else
946 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NO_MEMORY);
947 }
948 else if (offRead == cbObj)
949 rc = dbgcGdbStubCtxReplySend(pThis, "l", sizeof("l") - 1);
950 else
951 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
952
953 return rc;
954}
955
956
957/**
958 * Parses the annex:offset,length part of a 'qXfer:object:read:...' request.
959 *
960 * @returns Status code.
961 * @param pbArgs Start of the arguments beginning with annex.
962 * @param cbArgs Number of bytes remaining for the arguments.
963 * @param ppchAnnex Where to store the pointer to the beginning of the annex on success.
964 * @param pcchAnnex Where to store the number of characters for the annex on success.
965 * @param poffRead Where to store the offset on success.
966 * @param pcbRead Where to store the length on success.
967 */
968static int dbgcGdbStubCtxPktProcessQueryXferParseAnnexOffLen(const uint8_t *pbArgs, size_t cbArgs, const char **ppchAnnex, size_t *pcchAnnex,
969 uint32_t *poffRead, size_t *pcbRead)
970{
971 int rc = VINF_SUCCESS;
972 const uint8_t *pbSep = (const uint8_t *)memchr(pbArgs, ':', cbArgs);
973 if (pbSep)
974 {
975 *ppchAnnex = (const char *)pbArgs;
976 *pcchAnnex = pbSep - pbArgs;
977
978 pbSep++;
979 cbArgs -= *pcchAnnex + 1;
980
981 uint64_t u64Tmp = 0;
982 const uint8_t *pbLenStart = NULL;
983 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbSep, cbArgs, &u64Tmp, ',', &pbLenStart);
984 if ( RT_SUCCESS(rc)
985 && (uint32_t)u64Tmp == u64Tmp)
986 {
987 *poffRead = (uint32_t)u64Tmp;
988 cbArgs -= pbLenStart - pbSep;
989
990 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbLenStart + 1, cbArgs, &u64Tmp, '#', &pbLenStart);
991 if ( RT_SUCCESS(rc)
992 && (size_t)u64Tmp == u64Tmp)
993 *pcbRead = (size_t)u64Tmp;
994 else
995 rc = VERR_NET_PROTOCOL_ERROR;
996 }
997 else
998 rc = VERR_NET_PROTOCOL_ERROR;
999 }
1000 else
1001 rc = VERR_NET_PROTOCOL_ERROR;
1002
1003 return rc;
1004}
1005
1006
1007#define DBGREG_DESC_INIT_INT64(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 64, "int64", NULL }
1008#define DBGREG_DESC_INIT_INT32(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 32, "int32", NULL }
1009#define DBGREG_DESC_INIT_DATA_PTR64(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 64, "data_ptr", NULL }
1010#define DBGREG_DESC_INIT_CODE_PTR64(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 64, "code_ptr", NULL }
1011#define DBGREG_DESC_INIT_DATA_PTR32(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 32, "data_ptr", NULL }
1012#define DBGREG_DESC_INIT_CODE_PTR32(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 32, "code_ptr", NULL }
1013#define DBGREG_DESC_INIT_X87(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 80, "i387_ext", NULL }
1014#define DBGREG_DESC_INIT_X87_CTRL(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 32, "int", "float" }
1015
1016
1017/**
1018 * amd64 GDB register set.
1019 */
1020static const GDBREGDESC g_aGdbRegs64[] =
1021{
1022 DBGREG_DESC_INIT_INT64( "rax", DBGFREG_RAX),
1023 DBGREG_DESC_INIT_INT64( "rbx", DBGFREG_RBX),
1024 DBGREG_DESC_INIT_INT64( "rcx", DBGFREG_RCX),
1025 DBGREG_DESC_INIT_INT64( "rdx", DBGFREG_RDX),
1026 DBGREG_DESC_INIT_INT64( "rsi", DBGFREG_RSI),
1027 DBGREG_DESC_INIT_INT64( "rdi", DBGFREG_RDI),
1028 DBGREG_DESC_INIT_DATA_PTR64("rbp", DBGFREG_RBP),
1029 DBGREG_DESC_INIT_DATA_PTR64("rsp", DBGFREG_RSP),
1030 DBGREG_DESC_INIT_INT64( "r8", DBGFREG_R8),
1031 DBGREG_DESC_INIT_INT64( "r9", DBGFREG_R9),
1032 DBGREG_DESC_INIT_INT64( "r10", DBGFREG_R10),
1033 DBGREG_DESC_INIT_INT64( "r11", DBGFREG_R11),
1034 DBGREG_DESC_INIT_INT64( "r12", DBGFREG_R12),
1035 DBGREG_DESC_INIT_INT64( "r13", DBGFREG_R13),
1036 DBGREG_DESC_INIT_INT64( "r14", DBGFREG_R14),
1037 DBGREG_DESC_INIT_INT64( "r15", DBGFREG_R15),
1038 DBGREG_DESC_INIT_CODE_PTR64("rip", DBGFREG_RIP),
1039 DBGREG_DESC_INIT_INT32( "eflags", DBGFREG_FLAGS),
1040 DBGREG_DESC_INIT_INT32( "cs", DBGFREG_CS),
1041 DBGREG_DESC_INIT_INT32( "ss", DBGFREG_SS),
1042 DBGREG_DESC_INIT_INT32( "ds", DBGFREG_DS),
1043 DBGREG_DESC_INIT_INT32( "es", DBGFREG_ES),
1044 DBGREG_DESC_INIT_INT32( "fs", DBGFREG_FS),
1045 DBGREG_DESC_INIT_INT32( "gs", DBGFREG_GS),
1046
1047 DBGREG_DESC_INIT_X87( "st0", DBGFREG_ST0),
1048 DBGREG_DESC_INIT_X87( "st1", DBGFREG_ST1),
1049 DBGREG_DESC_INIT_X87( "st2", DBGFREG_ST2),
1050 DBGREG_DESC_INIT_X87( "st3", DBGFREG_ST3),
1051 DBGREG_DESC_INIT_X87( "st4", DBGFREG_ST4),
1052 DBGREG_DESC_INIT_X87( "st5", DBGFREG_ST5),
1053 DBGREG_DESC_INIT_X87( "st6", DBGFREG_ST6),
1054 DBGREG_DESC_INIT_X87( "st7", DBGFREG_ST7),
1055
1056 DBGREG_DESC_INIT_X87_CTRL( "fctrl", DBGFREG_FCW),
1057 DBGREG_DESC_INIT_X87_CTRL( "fstat", DBGFREG_FSW),
1058 DBGREG_DESC_INIT_X87_CTRL( "ftag", DBGFREG_FTW),
1059 DBGREG_DESC_INIT_X87_CTRL( "fop", DBGFREG_FOP),
1060 DBGREG_DESC_INIT_X87_CTRL( "fioff", DBGFREG_FPUIP),
1061 DBGREG_DESC_INIT_X87_CTRL( "fiseg", DBGFREG_FPUCS),
1062 DBGREG_DESC_INIT_X87_CTRL( "fooff", DBGFREG_FPUDP),
1063 DBGREG_DESC_INIT_X87_CTRL( "foseg", DBGFREG_FPUDS)
1064};
1065
1066
1067/**
1068 * i386 GDB register set.
1069 */
1070static const GDBREGDESC g_aGdbRegs32[] =
1071{
1072 DBGREG_DESC_INIT_INT32( "eax", DBGFREG_EAX),
1073 DBGREG_DESC_INIT_INT32( "ebx", DBGFREG_EBX),
1074 DBGREG_DESC_INIT_INT32( "ecx", DBGFREG_ECX),
1075 DBGREG_DESC_INIT_INT32( "edx", DBGFREG_EDX),
1076 DBGREG_DESC_INIT_INT32( "esi", DBGFREG_ESI),
1077 DBGREG_DESC_INIT_INT32( "edi", DBGFREG_EDI),
1078 DBGREG_DESC_INIT_DATA_PTR32("ebp", DBGFREG_EBP),
1079 DBGREG_DESC_INIT_DATA_PTR32("esp", DBGFREG_ESP),
1080 DBGREG_DESC_INIT_CODE_PTR32("eip", DBGFREG_EIP),
1081 DBGREG_DESC_INIT_INT32( "eflags", DBGFREG_FLAGS),
1082 DBGREG_DESC_INIT_INT32( "cs", DBGFREG_CS),
1083 DBGREG_DESC_INIT_INT32( "ss", DBGFREG_SS),
1084 DBGREG_DESC_INIT_INT32( "ds", DBGFREG_DS),
1085 DBGREG_DESC_INIT_INT32( "es", DBGFREG_ES),
1086 DBGREG_DESC_INIT_INT32( "fs", DBGFREG_FS),
1087 DBGREG_DESC_INIT_INT32( "gs", DBGFREG_GS),
1088
1089 DBGREG_DESC_INIT_X87( "st0", DBGFREG_ST0),
1090 DBGREG_DESC_INIT_X87( "st1", DBGFREG_ST1),
1091 DBGREG_DESC_INIT_X87( "st2", DBGFREG_ST2),
1092 DBGREG_DESC_INIT_X87( "st3", DBGFREG_ST3),
1093 DBGREG_DESC_INIT_X87( "st4", DBGFREG_ST4),
1094 DBGREG_DESC_INIT_X87( "st5", DBGFREG_ST5),
1095 DBGREG_DESC_INIT_X87( "st6", DBGFREG_ST6),
1096 DBGREG_DESC_INIT_X87( "st7", DBGFREG_ST7),
1097
1098 DBGREG_DESC_INIT_X87_CTRL( "fctrl", DBGFREG_FCW),
1099 DBGREG_DESC_INIT_X87_CTRL( "fstat", DBGFREG_FSW),
1100 DBGREG_DESC_INIT_X87_CTRL( "ftag", DBGFREG_FTW),
1101 DBGREG_DESC_INIT_X87_CTRL( "fop", DBGFREG_FOP),
1102 DBGREG_DESC_INIT_X87_CTRL( "fioff", DBGFREG_FPUIP),
1103 DBGREG_DESC_INIT_X87_CTRL( "fiseg", DBGFREG_FPUCS),
1104 DBGREG_DESC_INIT_X87_CTRL( "fooff", DBGFREG_FPUDP),
1105 DBGREG_DESC_INIT_X87_CTRL( "foseg", DBGFREG_FPUDS)
1106};
1107
1108#undef DBGREG_DESC_INIT_CODE_PTR64
1109#undef DBGREG_DESC_INIT_DATA_PTR64
1110#undef DBGREG_DESC_INIT_CODE_PTR32
1111#undef DBGREG_DESC_INIT_DATA_PTR32
1112#undef DBGREG_DESC_INIT_INT32
1113#undef DBGREG_DESC_INIT_INT64
1114#undef DBGREG_DESC_INIT_X87
1115#undef DBGREG_DESC_INIT_X87_CTRL
1116
1117
1118/**
1119 * Creates the target XML description.
1120 *
1121 * @returns Status code.
1122 * @param pThis The GDB stub context.
1123 */
1124static int dbgcGdbStubCtxTgtXmlDescCreate(PGDBSTUBCTX pThis)
1125{
1126 static const char s_szXmlTgtHdr64[] =
1127 "<?xml version=\"1.0\"?>\n"
1128 "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"
1129 "<target version=\"1.0\">\n"
1130 " <architecture>i386:x86-64</architecture>\n"
1131 " <feature name=\"org.gnu.gdb.i386.core\">\n";
1132 static const char s_szXmlTgtHdr32[] =
1133 "<?xml version=\"1.0\"?>\n"
1134 "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"
1135 "<target version=\"1.0\">\n"
1136 " <architecture>i386</architecture>\n"
1137 " <feature name=\"org.gnu.gdb.i386.core\">\n";
1138 static const char s_szXmlTgtFooter[] =
1139 " </feature>\n"
1140 "</target>\n";
1141
1142 int rc = VINF_SUCCESS;
1143
1144 pThis->pachTgtXmlDesc = (char *)RTStrAlloc(_32K);
1145 if (pThis->pachTgtXmlDesc)
1146 {
1147 size_t cbLeft = _32K;
1148 char *pachXmlCur = pThis->pachTgtXmlDesc;
1149 pThis->cbTgtXmlDesc = cbLeft;
1150
1151 rc = RTStrCatP(&pachXmlCur, &cbLeft, pThis->paRegs == &g_aGdbRegs64[0] ? &s_szXmlTgtHdr64[0] : &s_szXmlTgtHdr32[0]);
1152 if (RT_SUCCESS(rc))
1153 {
1154 /* Register */
1155 for (uint32_t i = 0; i < pThis->cRegs && RT_SUCCESS(rc); i++)
1156 {
1157 const struct GDBREGDESC *pReg = &pThis->paRegs[i];
1158
1159 ssize_t cchStr = 0;
1160 if (pReg->pszGroup)
1161 cchStr = RTStrPrintf2(pachXmlCur, cbLeft,
1162 "<reg name=\"%s\" bitsize=\"%u\" regnum=\"%u\" type=\"%s\" group=\"%s\"/>\n",
1163 pReg->pszName, pReg->cBits, i, pReg->pszType, pReg->pszGroup);
1164 else
1165 cchStr = RTStrPrintf2(pachXmlCur, cbLeft,
1166 "<reg name=\"%s\" bitsize=\"%u\" regnum=\"%u\" type=\"%s\"/>\n",
1167 pReg->pszName, pReg->cBits, i, pReg->pszType);
1168
1169 if (cchStr > 0)
1170 {
1171 pachXmlCur += cchStr;
1172 cbLeft -= cchStr;
1173 }
1174 else
1175 rc = VERR_BUFFER_OVERFLOW;
1176 }
1177 }
1178
1179 if (RT_SUCCESS(rc))
1180 rc = RTStrCatP(&pachXmlCur, &cbLeft, &s_szXmlTgtFooter[0]);
1181
1182 pThis->cbTgtXmlDesc -= cbLeft;
1183 }
1184 else
1185 rc = VERR_NO_MEMORY;
1186
1187 return rc;
1188}
1189
1190
1191/**
1192 * Returns the GDB register descriptor describing the given DBGF register enum.
1193 *
1194 * @returns Pointer to the GDB register descriptor or NULL if not found.
1195 * @param pThis The GDB stub context.
1196 * @param idxReg The register to look for.
1197 */
1198static const GDBREGDESC *dbgcGdbStubRegGet(PGDBSTUBCTX pThis, uint32_t idxReg)
1199{
1200 if (RT_LIKELY(idxReg < pThis->cRegs))
1201 return &pThis->paRegs[idxReg];
1202
1203 return NULL;
1204}
1205
1206
1207/**
1208 * Processes the 'C' query (query current thread ID).
1209 *
1210 * @returns Status code.
1211 * @param pThis The GDB stub context.
1212 * @param pbArgs Pointer to the start of the arguments in the packet.
1213 * @param cbArgs Size of arguments in bytes.
1214 */
1215static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryThreadId(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1216{
1217 RT_NOREF(pbArgs, cbArgs);
1218
1219 int rc = VERR_BUFFER_OVERFLOW;
1220 char achReply[32];
1221 ssize_t cchStr = RTStrPrintf(&achReply[0], sizeof(achReply), "QC %02x", pThis->Dbgc.idCpu + 1);
1222 if (cchStr > 0)
1223 rc = dbgcGdbStubCtxReplySend(pThis, &achReply[0], cchStr);
1224
1225 return rc;
1226}
1227
1228
1229/**
1230 * Processes the 'Attached' query.
1231 *
1232 * @returns Status code.
1233 * @param pThis The GDB stub context.
1234 * @param pbArgs Pointer to the start of the arguments in the packet.
1235 * @param cbArgs Size of arguments in bytes.
1236 */
1237static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryAttached(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1238{
1239 RT_NOREF(pbArgs, cbArgs);
1240
1241 /* We always report attached so that the VM doesn't get killed when GDB quits. */
1242 uint8_t bAttached = '1';
1243 return dbgcGdbStubCtxReplySend(pThis, &bAttached, sizeof(bAttached));
1244}
1245
1246
1247/**
1248 * Processes the 'Xfer:features:read' query.
1249 *
1250 * @returns Status code.
1251 * @param pThis The GDB stub context.
1252 * @param pbArgs Pointer to the start of the arguments in the packet.
1253 * @param cbArgs Size of arguments in bytes.
1254 */
1255static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryXferFeatRead(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1256{
1257 /* Skip the : following the Xfer:features:read start. */
1258 if ( cbArgs < 1
1259 || pbArgs[0] != ':')
1260 return VERR_NET_PROTOCOL_ERROR;
1261
1262 cbArgs--;
1263 pbArgs++;
1264
1265 int rc = VINF_SUCCESS;
1266 if (pThis->fFeatures & GDBSTUBCTX_FEATURES_F_TGT_DESC)
1267 {
1268 /* Create the target XML description if not existing. */
1269 if (!pThis->pachTgtXmlDesc)
1270 rc = dbgcGdbStubCtxTgtXmlDescCreate(pThis);
1271
1272 if (RT_SUCCESS(rc))
1273 {
1274 /* Parse annex, offset and length and return the data. */
1275 const char *pchAnnex = NULL;
1276 size_t cchAnnex = 0;
1277 uint32_t offRead = 0;
1278 size_t cbRead = 0;
1279
1280 rc = dbgcGdbStubCtxPktProcessQueryXferParseAnnexOffLen(pbArgs, cbArgs,
1281 &pchAnnex, &cchAnnex,
1282 &offRead, &cbRead);
1283 if (RT_SUCCESS(rc))
1284 {
1285 /* Check whether the annex is supported. */
1286 if ( cchAnnex == sizeof("target.xml") - 1
1287 && !memcmp(pchAnnex, "target.xml", cchAnnex))
1288 rc = dbgcGdbStubCtxQueryXferReadReply(pThis, offRead, cbRead, (const uint8_t *)pThis->pachTgtXmlDesc,
1289 pThis->cbTgtXmlDesc);
1290 else
1291 rc = dbgcGdbStubCtxReplySendErr(pThis, 0);
1292 }
1293 else
1294 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1295 }
1296 else
1297 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1298 }
1299 else
1300 rc = dbgcGdbStubCtxReplySend(pThis, NULL, 0); /* Not supported. */
1301
1302 return rc;
1303}
1304
1305
1306/**
1307 * Processes the 'Rcmd' query.
1308 *
1309 * @returns Status code.
1310 * @param pThis The GDB stub context.
1311 * @param pbArgs Pointer to the start of the arguments in the packet.
1312 * @param cbArgs Size of arguments in bytes.
1313 */
1314static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryRcmd(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1315{
1316 /* Skip the , following the qRcmd start. */
1317 if ( cbArgs < 1
1318 || pbArgs[0] != ',')
1319 return VERR_NET_PROTOCOL_ERROR;
1320
1321 cbArgs--;
1322 pbArgs++;
1323
1324 /* Decode the command. */
1325 /** @todo Make this dynamic. */
1326 char szCmd[_4K];
1327 RT_ZERO(szCmd);
1328
1329 if (cbArgs / 2 >= sizeof(szCmd))
1330 return VERR_NET_PROTOCOL_ERROR;
1331
1332 size_t cbDecoded = 0;
1333 int rc = RTStrConvertHexBytesEx((const char *)pbArgs, &szCmd[0], sizeof(szCmd), 0 /*fFlags*/,
1334 NULL /* ppszNext */, &cbDecoded);
1335 if (rc == VWRN_TRAILING_CHARS)
1336 rc = VINF_SUCCESS;
1337 if (RT_SUCCESS(rc))
1338 {
1339 szCmd[cbDecoded] = '\0'; /* Ensure zero termination. */
1340
1341 pThis->fOutput = false;
1342 rc = dbgcEvalCommand(&pThis->Dbgc, &szCmd[0], cbDecoded, false /*fNoExecute*/);
1343 dbgcGdbStubCtxReplySendOk(pThis);
1344 if ( rc != VERR_DBGC_QUIT
1345 && rc != VWRN_DBGC_CMD_PENDING)
1346 rc = VINF_SUCCESS; /* ignore other statuses */
1347 }
1348
1349 return rc;
1350}
1351
1352
1353/**
1354 * Worker for both 'qfThreadInfo' and 'qsThreadInfo'.
1355 *
1356 * @returns VBox status code.
1357 * @param pThis The GDB stub context.
1358 */
1359static int dbgcGdbStubCtxPktProcessQueryThreadInfoWorker(PGDBSTUBCTX pThis)
1360{
1361 int rc = dbgcGdbStubCtxReplySendBegin(pThis);
1362 if (RT_SUCCESS(rc))
1363 {
1364 uint8_t bReplyStart = { 'm' };
1365 rc = dbgcGdbStubCtxReplySendData(pThis, &bReplyStart, sizeof(bReplyStart));
1366 if (RT_SUCCESS(rc))
1367 {
1368 char achReply[32];
1369 ssize_t cchStr = RTStrPrintf(&achReply[0], sizeof(achReply), "%02x", pThis->idCpuNextThrdInfoQuery + 1);
1370 if (cchStr <= 0)
1371 rc = VERR_BUFFER_OVERFLOW;
1372
1373 if (RT_SUCCESS(rc))
1374 rc = dbgcGdbStubCtxReplySendData(pThis, &achReply[0], cchStr);
1375 pThis->idCpuNextThrdInfoQuery++;
1376 }
1377
1378 rc = dbgcGdbStubCtxReplySendEnd(pThis);
1379 }
1380
1381 return rc;
1382}
1383
1384
1385/**
1386 * Processes the 'fThreadInfo' query.
1387 *
1388 * @returns Status code.
1389 * @param pThis The GDB stub context.
1390 * @param pbArgs Pointer to the start of the arguments in the packet.
1391 * @param cbArgs Size of arguments in bytes.
1392 */
1393static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryThreadInfoStart(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1394{
1395 RT_NOREF(pbArgs, cbArgs);
1396
1397 pThis->idCpuNextThrdInfoQuery = 0;
1398 pThis->fInThrdInfoQuery = true;
1399 return dbgcGdbStubCtxPktProcessQueryThreadInfoWorker(pThis);
1400}
1401
1402
1403/**
1404 * Processes the 'fThreadInfo' query.
1405 *
1406 * @returns Status code.
1407 * @param pThis The GDB stub context.
1408 * @param pbArgs Pointer to the start of the arguments in the packet.
1409 * @param cbArgs Size of arguments in bytes.
1410 */
1411static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryThreadInfoCont(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1412{
1413 RT_NOREF(pbArgs, cbArgs);
1414
1415 /* If we are in a thread info query we just send the end of list specifier (all thread IDs where sent previously already). */
1416 if (!pThis->fInThrdInfoQuery)
1417 return dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1418
1419 VMCPUID cCpus = DBGFR3CpuGetCount(pThis->Dbgc.pUVM);
1420 if (pThis->idCpuNextThrdInfoQuery == cCpus)
1421 {
1422 pThis->fInThrdInfoQuery = false;
1423 uint8_t bEoL = 'l';
1424 return dbgcGdbStubCtxReplySend(pThis, &bEoL, sizeof(bEoL));
1425 }
1426
1427 return dbgcGdbStubCtxPktProcessQueryThreadInfoWorker(pThis);
1428}
1429
1430
1431/**
1432 * Processes the 'ThreadExtraInfo' query.
1433 *
1434 * @returns Status code.
1435 * @param pThis The GDB stub context.
1436 * @param pbArgs Pointer to the start of the arguments in the packet.
1437 * @param cbArgs Size of arguments in bytes.
1438 */
1439static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessQueryThreadExtraInfo(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1440{
1441 /* Skip the , following the qThreadExtraInfo start. */
1442 if ( cbArgs < 1
1443 || pbArgs[0] != ',')
1444 return VERR_NET_PROTOCOL_ERROR;
1445
1446 cbArgs--;
1447 pbArgs++;
1448
1449 /* We know there is an # character denoting the end so the following must return with VWRN_TRAILING_CHARS. */
1450 VMCPUID idCpu;
1451 int rc = RTStrToUInt32Ex((const char *)pbArgs, NULL /*ppszNext*/, 16, &idCpu);
1452 if ( rc == VWRN_TRAILING_CHARS
1453 && idCpu > 0)
1454 {
1455 idCpu--;
1456
1457 VMCPUID cCpus = DBGFR3CpuGetCount(pThis->Dbgc.pUVM);
1458 if (idCpu < cCpus)
1459 {
1460 const char *pszCpuState = DBGFR3CpuGetState(pThis->Dbgc.pUVM, idCpu);
1461 size_t cchCpuState = strlen(pszCpuState);
1462
1463 if (!pszCpuState)
1464 pszCpuState = "DBGFR3CpuGetState() -> NULL";
1465
1466 rc = dbgcGdbStubCtxReplySendBegin(pThis);
1467 if (RT_SUCCESS(rc))
1468 {
1469 /* Convert the characters to hex. */
1470 const char *pachCur = pszCpuState;
1471
1472 while ( cchCpuState
1473 && RT_SUCCESS(rc))
1474 {
1475 uint8_t achHex[512 + 1];
1476 size_t cbThisSend = RT_MIN((sizeof(achHex) - 1) / 2, cchCpuState); /* Each character needs two bytes. */
1477
1478 rc = dbgcGdbStubCtxEncodeBinaryAsHex(&achHex[0], cbThisSend * 2 + 1, pachCur, cbThisSend);
1479 if (RT_SUCCESS(rc))
1480 rc = dbgcGdbStubCtxReplySendData(pThis, &achHex[0], cbThisSend * 2);
1481
1482 pachCur += cbThisSend;
1483 cchCpuState -= cbThisSend;
1484 }
1485
1486 dbgcGdbStubCtxReplySendEnd(pThis);
1487 }
1488 }
1489 else
1490 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1491 }
1492 else if ( RT_SUCCESS(rc)
1493 || !idCpu)
1494 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1495
1496 return rc;
1497}
1498
1499
1500/**
1501 * List of supported query packets.
1502 */
1503static const GDBSTUBQPKTPROC g_aQPktProcs[] =
1504{
1505#define GDBSTUBQPKTPROC_INIT(a_Name, a_pfnProc) { a_Name, sizeof(a_Name) - 1, a_pfnProc }
1506 GDBSTUBQPKTPROC_INIT("C", dbgcGdbStubCtxPktProcessQueryThreadId),
1507 GDBSTUBQPKTPROC_INIT("Attached", dbgcGdbStubCtxPktProcessQueryAttached),
1508 GDBSTUBQPKTPROC_INIT("TStatus", dbgcGdbStubCtxPktProcessQueryTStatus),
1509 GDBSTUBQPKTPROC_INIT("Supported", dbgcGdbStubCtxPktProcessQuerySupported),
1510 GDBSTUBQPKTPROC_INIT("Xfer:features:read", dbgcGdbStubCtxPktProcessQueryXferFeatRead),
1511 GDBSTUBQPKTPROC_INIT("Rcmd", dbgcGdbStubCtxPktProcessQueryRcmd),
1512 GDBSTUBQPKTPROC_INIT("fThreadInfo", dbgcGdbStubCtxPktProcessQueryThreadInfoStart),
1513 GDBSTUBQPKTPROC_INIT("sThreadInfo", dbgcGdbStubCtxPktProcessQueryThreadInfoCont),
1514 GDBSTUBQPKTPROC_INIT("ThreadExtraInfo", dbgcGdbStubCtxPktProcessQueryThreadExtraInfo),
1515#undef GDBSTUBQPKTPROC_INIT
1516};
1517
1518
1519/**
1520 * Processes a 'q' packet, sending the appropriate reply.
1521 *
1522 * @returns Status code.
1523 * @param pThis The GDB stub context.
1524 * @param pbQuery The query packet data (without the 'q').
1525 * @param cbQuery Size of the remaining query packet in bytes.
1526 */
1527static int dbgcGdbStubCtxPktProcessQuery(PGDBSTUBCTX pThis, const uint8_t *pbQuery, size_t cbQuery)
1528{
1529 /* Search the query and execute the processor or return an empty reply if not supported. */
1530 for (uint32_t i = 0; i < RT_ELEMENTS(g_aQPktProcs); i++)
1531 {
1532 size_t cbCmp = g_aQPktProcs[i].cchName < cbQuery ? g_aQPktProcs[i].cchName : cbQuery;
1533
1534 if (!memcmp(pbQuery, g_aQPktProcs[i].pszName, cbCmp))
1535 return g_aQPktProcs[i].pfnProc(pThis, pbQuery + cbCmp, cbQuery - cbCmp);
1536 }
1537
1538 return dbgcGdbStubCtxReplySend(pThis, NULL, 0);
1539}
1540
1541
1542/**
1543 * Processes a 'vCont[;action[:thread-id]]' packet.
1544 *
1545 * @returns Status code.
1546 * @param pThis The GDB stub context.
1547 * @param pbArgs Pointer to the start of the arguments in the packet.
1548 * @param cbArgs Size of arguments in bytes.
1549 */
1550static DECLCALLBACK(int) dbgcGdbStubCtxPktProcessVCont(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1551{
1552 int rc = VINF_SUCCESS;
1553
1554 /* Skip the ; following the identifier. */
1555 if ( cbArgs < 2
1556 || pbArgs[0] != ';')
1557 return dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1558
1559 pbArgs++;
1560 cbArgs--;
1561
1562 /** @todo For now we don't care about multiple threads and ignore thread IDs and multiple actions. */
1563 switch (pbArgs[0])
1564 {
1565 case 'c':
1566 {
1567 if (DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL))
1568 DBGFR3Resume(pThis->Dbgc.pUVM, VMCPUID_ALL);
1569 break;
1570 }
1571 case 's':
1572 {
1573 PDBGFADDRESS pStackPop = NULL;
1574 RTGCPTR cbStackPop = 0;
1575 rc = DBGFR3StepEx(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGF_STEP_F_INTO, NULL,
1576 pStackPop, cbStackPop, 1 /*cMaxSteps*/);
1577 if (RT_FAILURE(rc))
1578 dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1579 break;
1580 }
1581 case 't':
1582 {
1583 if (!DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL))
1584 rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL);
1585 /* The reply will be send in the event loop. */
1586 break;
1587 }
1588 default:
1589 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1590 }
1591
1592 return rc;
1593}
1594
1595
1596/**
1597 * List of supported 'v<identifier>' packets.
1598 */
1599static const GDBSTUBVPKTPROC g_aVPktProcs[] =
1600{
1601#define GDBSTUBVPKTPROC_INIT(a_Name, a_pszReply, a_pfnProc) { a_Name, sizeof(a_Name) - 1, a_pszReply, sizeof(a_pszReply) - 1, a_pfnProc }
1602 GDBSTUBVPKTPROC_INIT("Cont", "vCont;s;c;t", dbgcGdbStubCtxPktProcessVCont)
1603#undef GDBSTUBVPKTPROC_INIT
1604};
1605
1606
1607/**
1608 * Processes a 'v<identifier>' packet, sending the appropriate reply.
1609 *
1610 * @returns Status code.
1611 * @param pThis The GDB stub context.
1612 * @param pbPktRem The remaining packet data (without the 'v').
1613 * @param cbPktRem Size of the remaining packet in bytes.
1614 */
1615static int dbgcGdbStubCtxPktProcessV(PGDBSTUBCTX pThis, const uint8_t *pbPktRem, size_t cbPktRem)
1616{
1617 /* Determine the end of the identifier, delimiters are '?', ';' or end of packet. */
1618 bool fQuery = false;
1619 const uint8_t *pbDelim = (const uint8_t *)memchr(pbPktRem, '?', cbPktRem);
1620 if (!pbDelim)
1621 pbDelim = (const uint8_t *)memchr(pbPktRem, ';', cbPktRem);
1622 else
1623 fQuery = true;
1624
1625 size_t cchId = 0;
1626 if (pbDelim) /* Delimiter found, calculate length. */
1627 cchId = pbDelim - pbPktRem;
1628 else /* Not found, size goes till end of packet. */
1629 cchId = cbPktRem;
1630
1631 /* Search the query and execute the processor or return an empty reply if not supported. */
1632 for (uint32_t i = 0; i < RT_ELEMENTS(g_aVPktProcs); i++)
1633 {
1634 PCGDBSTUBVPKTPROC pVProc = &g_aVPktProcs[i];
1635
1636 if ( pVProc->cchName == cchId
1637 && !memcmp(pbPktRem, pVProc->pszName, cchId))
1638 {
1639 /* Just send the static reply for a query and execute the processor for everything else. */
1640 if (fQuery)
1641 return dbgcGdbStubCtxReplySend(pThis, pVProc->pszReplyQ, pVProc->cchReplyQ);
1642
1643 /* Execute the handler. */
1644 return pVProc->pfnProc(pThis, pbPktRem + cchId, cbPktRem - cchId);
1645 }
1646 }
1647
1648 return dbgcGdbStubCtxReplySend(pThis, NULL, 0);
1649}
1650
1651
1652/**
1653 * Processes a 'H<op><thread-id>' packet, sending the appropriate reply.
1654 *
1655 * @returns Status code.
1656 * @param pThis The GDB stub context.
1657 * @param pbPktRem The remaining packet data (without the 'H').
1658 * @param cbPktRem Size of the remaining packet in bytes.
1659 */
1660static int dbgcGdbStubCtxPktProcessH(PGDBSTUBCTX pThis, const uint8_t *pbPktRem, size_t cbPktRem)
1661{
1662 int rc = VINF_SUCCESS;
1663
1664 if (*pbPktRem == 'g')
1665 {
1666 cbPktRem--;
1667 pbPktRem++;
1668
1669 /* We know there is an # character denoting the end so the following must return with VWRN_TRAILING_CHARS. */
1670 VMCPUID idCpu;
1671 rc = RTStrToUInt32Ex((const char *)pbPktRem, NULL /*ppszNext*/, 16, &idCpu);
1672 if ( rc == VWRN_TRAILING_CHARS
1673 && idCpu > 0)
1674 {
1675 idCpu--;
1676
1677 VMCPUID cCpus = DBGFR3CpuGetCount(pThis->Dbgc.pUVM);
1678 if (idCpu < cCpus)
1679 {
1680 pThis->Dbgc.idCpu = idCpu;
1681 rc = dbgcGdbStubCtxReplySendOk(pThis);
1682 }
1683 else
1684 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1685 }
1686 else
1687 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1688 }
1689 else /* Do not support the 'c' operation for now (will be handled through vCont later on anyway). */
1690 rc = dbgcGdbStubCtxReplySend(pThis, NULL, 0);
1691
1692 return rc;
1693}
1694
1695
1696/**
1697 * Processes a completely received packet.
1698 *
1699 * @returns Status code.
1700 * @param pThis The GDB stub context.
1701 */
1702static int dbgcGdbStubCtxPktProcess(PGDBSTUBCTX pThis)
1703{
1704 int rc = VINF_SUCCESS;
1705
1706 if (pThis->cbPkt >= 1)
1707 {
1708 switch (pThis->pbPktBuf[1])
1709 {
1710 case '!': /* Enabled extended mode. */
1711 {
1712 pThis->fExtendedMode = true;
1713 rc = dbgcGdbStubCtxReplySendOk(pThis);
1714 break;
1715 }
1716 case '?':
1717 {
1718 /* Return signal state. */
1719 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
1720 break;
1721 }
1722 case 's': /* Single step, response will be sent in the event loop. */
1723 {
1724 PDBGFADDRESS pStackPop = NULL;
1725 RTGCPTR cbStackPop = 0;
1726 rc = DBGFR3StepEx(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGF_STEP_F_INTO, NULL,
1727 pStackPop, cbStackPop, 1 /*cMaxSteps*/);
1728 if (RT_FAILURE(rc))
1729 dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1730 break;
1731 }
1732 case 'c': /* Continue, no response */
1733 {
1734 if (DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL))
1735 DBGFR3Resume(pThis->Dbgc.pUVM, VMCPUID_ALL);
1736 break;
1737 }
1738 case 'H':
1739 {
1740 rc = dbgcGdbStubCtxPktProcessH(pThis, &pThis->pbPktBuf[2], pThis->cbPkt - 1);
1741 break;
1742 }
1743 case 'T':
1744 {
1745 rc = dbgcGdbStubCtxReplySendOk(pThis);
1746 break;
1747 }
1748 case 'g': /* Read general registers. */
1749 {
1750 uint32_t idxRegMax = 0;
1751 size_t cbRegs = 0;
1752 for (;;)
1753 {
1754 const GDBREGDESC *pReg = &pThis->paRegs[idxRegMax++];
1755 cbRegs += pReg->cBits / 8;
1756 if (pReg->enmReg == DBGFREG_SS) /* Up to this seems to belong to the general register set. */
1757 break;
1758 }
1759
1760 size_t cbReplyPkt = cbRegs * 2 + 1; /* One byte needs two characters. */
1761 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbReplyPkt);
1762 if (RT_SUCCESS(rc))
1763 {
1764 size_t cbLeft = cbReplyPkt;
1765 uint8_t *pbReply = pThis->pbPktBuf;
1766
1767 for (uint32_t i = 0; i < idxRegMax && RT_SUCCESS(rc); i++)
1768 {
1769 const GDBREGDESC *pReg = &pThis->paRegs[i];
1770 size_t cbReg = pReg->cBits / 8;
1771 union
1772 {
1773 uint32_t u32;
1774 uint64_t u64;
1775 uint8_t au8[8];
1776 } RegVal;
1777
1778 if (pReg->cBits == 32)
1779 rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->enmReg, &RegVal.u32);
1780 else
1781 rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->enmReg, &RegVal.u64);
1782
1783 if (RT_SUCCESS(rc))
1784 rc = dbgcGdbStubCtxEncodeBinaryAsHex(pbReply, cbLeft, &RegVal.au8[0], cbReg);
1785
1786 pbReply += cbReg * 2;
1787 cbLeft -= cbReg * 2;
1788 }
1789
1790 if (RT_SUCCESS(rc))
1791 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbReplyPkt);
1792 else
1793 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1794 }
1795
1796 break;
1797 }
1798 case 'm': /* Read memory. */
1799 {
1800 uint64_t GdbTgtAddr = 0;
1801 const uint8_t *pbPktSep = NULL;
1802
1803 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &GdbTgtAddr,
1804 ',', &pbPktSep);
1805 if (RT_SUCCESS(rc))
1806 {
1807 size_t cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1808 uint64_t cbRead = 0;
1809 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, pThis->cbPkt - 1 - cbProcessed - 1, &cbRead, GDBSTUB_PKT_END, NULL);
1810 if (RT_SUCCESS(rc))
1811 {
1812 size_t cbReplyPkt = cbRead * 2 + 1; /* One byte needs two characters. */
1813
1814 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbReplyPkt);
1815 if (RT_SUCCESS(rc))
1816 {
1817 uint8_t *pbPktBuf = pThis->pbPktBuf;
1818 size_t cbPktBufLeft = cbReplyPkt;
1819 DBGFADDRESS AddrRead;
1820
1821 DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrRead, GdbTgtAddr);
1822
1823 while ( cbRead
1824 && RT_SUCCESS(rc))
1825 {
1826 uint8_t abTmp[_4K];
1827 size_t cbThisRead = RT_MIN(cbRead, sizeof(abTmp));
1828
1829 rc = DBGFR3MemRead(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &AddrRead, &abTmp[0], cbThisRead);
1830 if (RT_FAILURE(rc))
1831 break;
1832
1833 rc = dbgcGdbStubCtxEncodeBinaryAsHex(pbPktBuf, cbPktBufLeft, &abTmp[0], cbThisRead);
1834 if (RT_FAILURE(rc))
1835 break;
1836
1837 DBGFR3AddrAdd(&AddrRead, cbThisRead);
1838 cbRead -= cbThisRead;
1839 pbPktBuf += cbThisRead;
1840 cbPktBufLeft -= cbThisRead;
1841 }
1842
1843 if (RT_SUCCESS(rc))
1844 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbReplyPkt);
1845 else
1846 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1847 }
1848 else
1849 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1850 }
1851 else
1852 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1853 }
1854 else
1855 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1856 break;
1857 }
1858 case 'M': /* Write memory. */
1859 {
1860 uint64_t GdbTgtAddr = 0;
1861 const uint8_t *pbPktSep = NULL;
1862
1863 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &GdbTgtAddr,
1864 ',', &pbPktSep);
1865 if (RT_SUCCESS(rc))
1866 {
1867 size_t cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1868 uint64_t cbWrite = 0;
1869 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, pThis->cbPkt - 1 - cbProcessed - 1, &cbWrite, ':', &pbPktSep);
1870 if (RT_SUCCESS(rc))
1871 {
1872 cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1873 const uint8_t *pbDataCur = pbPktSep + 1;
1874 size_t cbDataLeft = pThis->cbPkt - 1 - cbProcessed - 1 - 1;
1875 DBGFADDRESS AddrWrite;
1876
1877 DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrWrite, GdbTgtAddr);
1878
1879 while ( cbWrite
1880 && RT_SUCCESS(rc))
1881 {
1882 uint8_t abTmp[_4K];
1883 size_t cbThisWrite = RT_MIN(cbWrite, sizeof(abTmp));
1884 size_t cbDecoded = 0;
1885
1886 rc = dbgcGdbStubCtxParseHexStringAsByteBuf(pbDataCur, cbDataLeft, &abTmp[0], cbThisWrite, &cbDecoded);
1887 if (!rc)
1888 rc = DBGFR3MemWrite(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &AddrWrite, &abTmp[0], cbThisWrite);
1889
1890 DBGFR3AddrAdd(&AddrWrite, cbThisWrite);
1891 cbWrite -= cbThisWrite;
1892 pbDataCur += cbDecoded;
1893 cbDataLeft -= cbDecoded;
1894 }
1895
1896 if (RT_SUCCESS(rc))
1897 rc = dbgcGdbStubCtxReplySendOk(pThis);
1898 else
1899 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1900 }
1901 else
1902 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1903 }
1904 else
1905 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1906 break;
1907 }
1908 case 'p': /* Read a single register */
1909 {
1910 uint64_t uReg = 0;
1911 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &uReg,
1912 GDBSTUB_PKT_END, NULL);
1913 if (RT_SUCCESS(rc))
1914 {
1915 DBGFREGVAL RegVal;
1916 DBGFREGVALTYPE enmType;
1917 const GDBREGDESC *pReg = dbgcGdbStubRegGet(pThis, uReg);
1918 if (RT_LIKELY(pReg))
1919 {
1920 rc = DBGFR3RegNmQuery(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->pszName, &RegVal, &enmType);
1921 if (RT_SUCCESS(rc))
1922 {
1923 size_t cbReg = pReg->cBits / 8;
1924 size_t cbReplyPkt = cbReg * 2 + 1; /* One byte needs two characters. */
1925
1926 /* Encode data and send. */
1927 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbReplyPkt);
1928 if (RT_SUCCESS(rc))
1929 {
1930 rc = dbgcGdbStubCtxEncodeBinaryAsHex(pThis->pbPktBuf, pThis->cbPktBufMax, &RegVal.au8[0], cbReg);
1931 if (RT_SUCCESS(rc))
1932 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbReplyPkt);
1933 else
1934 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1935 }
1936 else
1937 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1938 }
1939 else
1940 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1941 }
1942 else
1943 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1944 }
1945 else
1946 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1947 break;
1948 }
1949 case 'P': /* Write a single register */
1950 {
1951 uint64_t uReg = 0;
1952 const uint8_t *pbPktSep = NULL;
1953 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &uReg,
1954 '=', &pbPktSep);
1955 if (RT_SUCCESS(rc))
1956 {
1957 const GDBREGDESC *pReg = dbgcGdbStubRegGet(pThis, uReg);
1958
1959 if (pReg)
1960 {
1961 DBGFREGVAL RegVal;
1962 DBGFREGVALTYPE enmValType = pReg->cBits == 64 ? DBGFREGVALTYPE_U64 : DBGFREGVALTYPE_U32;
1963 size_t cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1964 rc = dbgcGdbStubCtxParseHexStringAsByteBuf(pbPktSep + 1, pThis->cbPkt - 1 - cbProcessed - 1, &RegVal.au8[0], pReg->cBits / 8, NULL);
1965 if (RT_SUCCESS(rc))
1966 {
1967 rc = DBGFR3RegNmSet(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->pszName, &RegVal, enmValType);
1968 if (RT_SUCCESS(rc))
1969 rc = dbgcGdbStubCtxReplySendOk(pThis);
1970 else
1971 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1972 }
1973 }
1974 else
1975 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1976 }
1977 else
1978 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1979 break;
1980 }
1981 case 'Z': /* Insert a breakpoint/watchpoint. */
1982 {
1983 GDBSTUBTPTYPE enmTpType = GDBSTUBTPTYPE_INVALID;
1984 uint64_t GdbTgtTpAddr = 0;
1985 uint64_t uKind = 0;
1986
1987 rc = dbgcGdbStubCtxParseTpPktArgs(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &enmTpType, &GdbTgtTpAddr, &uKind);
1988 if (RT_SUCCESS(rc))
1989 {
1990 uint32_t iBp = 0;
1991 DBGFADDRESS BpAddr;
1992 DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &BpAddr, GdbTgtTpAddr);
1993
1994 switch (enmTpType)
1995 {
1996 case GDBSTUBTPTYPE_EXEC_SW:
1997 {
1998 rc = DBGFR3BpSetInt3(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &BpAddr,
1999 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/, &iBp);
2000 break;
2001 }
2002 case GDBSTUBTPTYPE_EXEC_HW:
2003 {
2004 rc = DBGFR3BpSetReg(pThis->Dbgc.pUVM, &BpAddr,
2005 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/,
2006 X86_DR7_RW_EO, 1 /*cb*/, &iBp);
2007 break;
2008 }
2009 case GDBSTUBTPTYPE_MEM_ACCESS:
2010 case GDBSTUBTPTYPE_MEM_READ:
2011 {
2012 rc = DBGFR3BpSetReg(pThis->Dbgc.pUVM, &BpAddr,
2013 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/,
2014 X86_DR7_RW_RW, uKind /*cb*/, &iBp);
2015 break;
2016 }
2017 case GDBSTUBTPTYPE_MEM_WRITE:
2018 {
2019 rc = DBGFR3BpSetReg(pThis->Dbgc.pUVM, &BpAddr,
2020 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/,
2021 X86_DR7_RW_WO, uKind /*cb*/, &iBp);
2022 break;
2023 }
2024 default:
2025 AssertMsgFailed(("Invalid trace point type %d\n", enmTpType));
2026 }
2027
2028 if (RT_SUCCESS(rc))
2029 {
2030 rc = dbgcBpAdd(&pThis->Dbgc, iBp, NULL /*pszCmd*/);
2031 if (RT_SUCCESS(rc))
2032 {
2033 rc = dbgcGdbStubTpRegister(pThis, enmTpType, GdbTgtTpAddr, uKind, iBp);
2034 if (RT_SUCCESS(rc))
2035 rc = dbgcGdbStubCtxReplySendOk(pThis);
2036 else
2037 dbgcBpDelete(&pThis->Dbgc, iBp);
2038 }
2039
2040 if (RT_FAILURE(rc))
2041 {
2042 DBGFR3BpClear(pThis->Dbgc.pUVM, iBp);
2043 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
2044 }
2045 }
2046 else
2047 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
2048 }
2049 else
2050 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
2051 break;
2052 }
2053 case 'z': /* Remove a breakpoint/watchpoint. */
2054 {
2055 GDBSTUBTPTYPE enmTpType = GDBSTUBTPTYPE_INVALID;
2056 uint64_t GdbTgtTpAddr = 0;
2057 uint64_t uKind = 0;
2058
2059 rc = dbgcGdbStubCtxParseTpPktArgs(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &enmTpType, &GdbTgtTpAddr, &uKind);
2060 if (RT_SUCCESS(rc))
2061 {
2062 PGDBSTUBTP pTp = dbgcGdbStubTpFind(pThis, enmTpType, GdbTgtTpAddr, uKind);
2063 if (pTp)
2064 {
2065 int rc2 = DBGFR3BpClear(pThis->Dbgc.pUVM, pTp->iBp);
2066 if (RT_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
2067 dbgcBpDelete(&pThis->Dbgc, pTp->iBp);
2068
2069 if (RT_SUCCESS(rc2))
2070 {
2071 dbgcGdbStubTpDeregister(pTp);
2072 rc = dbgcGdbStubCtxReplySendOk(pThis);
2073 }
2074 else
2075 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
2076 }
2077 else
2078 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NOT_FOUND);
2079 }
2080 else
2081 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
2082 break;
2083 }
2084 case 'q': /* Query packet */
2085 {
2086 rc = dbgcGdbStubCtxPktProcessQuery(pThis, &pThis->pbPktBuf[2], pThis->cbPkt - 1);
2087 break;
2088 }
2089 case 'v': /* Multiletter identifier (verbose?) */
2090 {
2091 rc = dbgcGdbStubCtxPktProcessV(pThis, &pThis->pbPktBuf[2], pThis->cbPkt - 1);
2092 break;
2093 }
2094 case 'R': /* Restart target. */
2095 {
2096 rc = dbgcGdbStubCtxReplySend(pThis, NULL, 0);
2097 break;
2098 }
2099 case 'k': /* Kill target. */
2100 {
2101 /* This is what the 'harakiri' command is doing. */
2102 for (;;)
2103 exit(126);
2104 break;
2105 }
2106 case 'D': /* Detach */
2107 {
2108 rc = dbgcGdbStubCtxReplySendOk(pThis);
2109 if (RT_SUCCESS(rc))
2110 rc = VERR_DBGC_QUIT;
2111 break;
2112 }
2113 default:
2114 /* Not supported, send empty reply. */
2115 rc = dbgcGdbStubCtxReplySend(pThis, NULL, 0);
2116 }
2117 }
2118
2119 return rc;
2120}
2121
2122
2123/**
2124 * Resets the packet buffer.
2125 *
2126 * @returns nothing.
2127 * @param pThis The GDB stub context.
2128 */
2129static void dbgcGdbStubCtxPktBufReset(PGDBSTUBCTX pThis)
2130{
2131 pThis->offPktBuf = 0;
2132 pThis->cbPkt = 0;
2133 pThis->cbChksumRecvLeft = 2;
2134}
2135
2136
2137/**
2138 * Resets the given GDB stub context to the initial state.
2139 *
2140 * @returns nothing.
2141 * @param pThis The GDB stub context.
2142 */
2143static void dbgcGdbStubCtxReset(PGDBSTUBCTX pThis)
2144{
2145 pThis->enmState = GDBSTUBRECVSTATE_PACKET_WAIT_FOR_START;
2146 dbgcGdbStubCtxPktBufReset(pThis);
2147}
2148
2149
2150/**
2151 * Searches for the start character in the current data buffer.
2152 *
2153 * @returns Status code.
2154 * @param pThis The GDB stub context.
2155 * @param cbData Number of new bytes in the packet buffer.
2156 * @param pcbProcessed Where to store the amount of bytes processed.
2157 */
2158static int dbgcGdbStubCtxPktBufSearchStart(PGDBSTUBCTX pThis, size_t cbData, size_t *pcbProcessed)
2159{
2160 int rc = VINF_SUCCESS;
2161 const uint8_t *pbStart = (const uint8_t *)memchr(pThis->pbPktBuf, GDBSTUB_PKT_START, cbData);
2162 if (pbStart)
2163 {
2164 /* Found the start character, align the start to the beginning of the packet buffer and advance the state machine. */
2165 memmove(pThis->pbPktBuf, pbStart, cbData - (pbStart - pThis->pbPktBuf));
2166 pThis->enmState = GDBSTUBRECVSTATE_PACKET_RECEIVE_BODY;
2167 *pcbProcessed = (uintptr_t)(pbStart - pThis->pbPktBuf);
2168 pThis->offPktBuf = 0;
2169 }
2170 else
2171 {
2172 /* Check for out of band characters. */
2173 if (memchr(pThis->pbPktBuf, GDBSTUB_OOB_INTERRUPT, cbData) != NULL)
2174 {
2175 /* Stop target and send packet to indicate the target has stopped. */
2176 if (!DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL))
2177 rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL);
2178 /* The reply will be send in the event loop. */
2179 }
2180
2181 /* Not found, ignore the received data and reset the packet buffer. */
2182 dbgcGdbStubCtxPktBufReset(pThis);
2183 *pcbProcessed = cbData;
2184 }
2185
2186 return rc;
2187}
2188
2189
2190/**
2191 * Searches for the end character in the current data buffer.
2192 *
2193 * @returns Status code.
2194 * @param pThis The GDB stub context.
2195 * @param cbData Number of new bytes in the packet buffer.
2196 * @param pcbProcessed Where to store the amount of bytes processed.
2197 */
2198static int dbgcGdbStubCtxPktBufSearchEnd(PGDBSTUBCTX pThis, size_t cbData, size_t *pcbProcessed)
2199{
2200 const uint8_t *pbEnd = (const uint8_t *)memchr(&pThis->pbPktBuf[pThis->offPktBuf], GDBSTUB_PKT_END, cbData);
2201 if (pbEnd)
2202 {
2203 /* Found the end character, next comes the checksum. */
2204 pThis->enmState = GDBSTUBRECVSTATE_PACKET_RECEIVE_CHECKSUM;
2205
2206 *pcbProcessed = (uintptr_t)(pbEnd - &pThis->pbPktBuf[pThis->offPktBuf]) + 1;
2207 pThis->offPktBuf += *pcbProcessed;
2208 pThis->cbPkt = pThis->offPktBuf - 1; /* Don't account for the start and end character. */
2209 }
2210 else
2211 {
2212 /* Not found, still in the middle of a packet. */
2213 /** @todo Look for out of band characters. */
2214 *pcbProcessed = cbData;
2215 pThis->offPktBuf += cbData;
2216 }
2217
2218 return VINF_SUCCESS;
2219}
2220
2221
2222/**
2223 * Processes the checksum.
2224 *
2225 * @returns Status code.
2226 * @param pThis The GDB stub context.
2227 * @param cbData Number of new bytes in the packet buffer.
2228 * @param pcbProcessed Where to store the amount of bytes processed.
2229 */
2230static int dbgcGdbStubCtxPktBufProcessChksum(PGDBSTUBCTX pThis, size_t cbData, size_t *pcbProcessed)
2231{
2232 int rc = VINF_SUCCESS;
2233 size_t cbChksumProcessed = (cbData < pThis->cbChksumRecvLeft) ? cbData : pThis->cbChksumRecvLeft;
2234
2235 pThis->cbChksumRecvLeft -= cbChksumProcessed;
2236 if (!pThis->cbChksumRecvLeft)
2237 {
2238 /* Verify checksum of the whole packet. */
2239 uint8_t uChkSum = dbgcGdbStubCtxChrToHex(pThis->pbPktBuf[pThis->offPktBuf]) << 4
2240 | dbgcGdbStubCtxChrToHex(pThis->pbPktBuf[pThis->offPktBuf + 1]);
2241
2242 uint8_t uSum = 0;
2243 for (size_t i = 1; i < pThis->cbPkt; i++)
2244 uSum += pThis->pbPktBuf[i];
2245
2246 if (uSum == uChkSum)
2247 {
2248 /* Checksum matches, send acknowledge and continue processing the complete payload. */
2249 char chAck = '+';
2250 rc = dbgcGdbStubCtxWrite(pThis, &chAck, sizeof(chAck));
2251 if (RT_SUCCESS(rc))
2252 rc = dbgcGdbStubCtxPktProcess(pThis);
2253 }
2254 else
2255 {
2256 /* Send NACK and reset for the next packet. */
2257 char chAck = '-';
2258 rc = dbgcGdbStubCtxWrite(pThis, &chAck, sizeof(chAck));
2259 }
2260
2261 dbgcGdbStubCtxReset(pThis);
2262 }
2263
2264 *pcbProcessed += cbChksumProcessed;
2265 return rc;
2266}
2267
2268
2269/**
2270 * Process read data in the packet buffer based on the current state.
2271 *
2272 * @returns Status code.
2273 * @param pThis The GDB stub context.
2274 * @param cbData Number of new bytes in the packet buffer.
2275 */
2276static int dbgcGdbStubCtxPktBufProcess(PGDBSTUBCTX pThis, size_t cbData)
2277{
2278 int rc = VINF_SUCCESS;
2279
2280 while ( cbData
2281 && RT_SUCCESS(rc))
2282 {
2283 size_t cbProcessed = 0;
2284
2285 switch (pThis->enmState)
2286 {
2287 case GDBSTUBRECVSTATE_PACKET_WAIT_FOR_START:
2288 {
2289 rc = dbgcGdbStubCtxPktBufSearchStart(pThis, cbData, &cbProcessed);
2290 break;
2291 }
2292 case GDBSTUBRECVSTATE_PACKET_RECEIVE_BODY:
2293 {
2294 rc = dbgcGdbStubCtxPktBufSearchEnd(pThis, cbData, &cbProcessed);
2295 break;
2296 }
2297 case GDBSTUBRECVSTATE_PACKET_RECEIVE_CHECKSUM:
2298 {
2299 rc = dbgcGdbStubCtxPktBufProcessChksum(pThis, cbData, &cbProcessed);
2300 break;
2301 }
2302 default:
2303 /* Should never happen. */
2304 rc = VERR_INTERNAL_ERROR;
2305 }
2306
2307 cbData -= cbProcessed;
2308 }
2309
2310 return rc;
2311}
2312
2313
2314/**
2315 * Receive data and processes complete packets.
2316 *
2317 * @returns Status code.
2318 * @param pThis The GDB stub context.
2319 */
2320static int dbgcGdbStubCtxRecv(PGDBSTUBCTX pThis)
2321{
2322 /*
2323 * Read in 32 bytes chunks for now (need some peek API to get the amount of bytes actually available
2324 * to make it a bit more optimized).
2325 */
2326 int rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, 32);
2327 if (RT_SUCCESS(rc))
2328 {
2329 size_t cbThisRead = 32;
2330 rc = pThis->Dbgc.pIo->pfnRead(pThis->Dbgc.pIo, &pThis->pbPktBuf[pThis->offPktBuf], cbThisRead, &cbThisRead);
2331 if (RT_SUCCESS(rc))
2332 rc = dbgcGdbStubCtxPktBufProcess(pThis, cbThisRead);
2333 }
2334
2335 return rc;
2336}
2337
2338
2339/**
2340 * Processes debugger events.
2341 *
2342 * @returns VBox status code.
2343 * @param pThis The GDB stub context data.
2344 * @param pEvent Pointer to event data.
2345 */
2346static int dbgcGdbStubCtxProcessEvent(PGDBSTUBCTX pThis, PCDBGFEVENT pEvent)
2347{
2348 /*
2349 * Process the event.
2350 */
2351 PDBGC pDbgc = &pThis->Dbgc;
2352 pThis->Dbgc.pszScratch = &pThis->Dbgc.achInput[0];
2353 pThis->Dbgc.iArg = 0;
2354 int rc = VINF_SUCCESS;
2355 switch (pEvent->enmType)
2356 {
2357 /*
2358 * The first part is events we have initiated with commands.
2359 */
2360 case DBGFEVENT_HALT_DONE:
2361 {
2362 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
2363 break;
2364 }
2365
2366
2367 /*
2368 * The second part is events which can occur at any time.
2369 */
2370 case DBGFEVENT_FATAL_ERROR:
2371 {
2372 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n",
2373 dbgcGetEventCtx(pEvent->enmCtx));
2374 if (RT_SUCCESS(rc))
2375 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2376 break;
2377 }
2378
2379 case DBGFEVENT_BREAKPOINT:
2380 case DBGFEVENT_BREAKPOINT_IO:
2381 case DBGFEVENT_BREAKPOINT_MMIO:
2382 case DBGFEVENT_BREAKPOINT_HYPER:
2383 {
2384 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);
2385 switch (rc)
2386 {
2387 case VERR_DBGC_BP_NOT_FOUND:
2388 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n",
2389 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2390 break;
2391
2392 case VINF_DBGC_BP_NO_COMMAND:
2393 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n",
2394 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2395 break;
2396
2397 case VINF_BUFFER_OVERFLOW:
2398 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",
2399 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2400 break;
2401
2402 default:
2403 break;
2404 }
2405 if (RT_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pUVM, VMCPUID_ALL))
2406 {
2407 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2408
2409 /* Set the resume flag to ignore the breakpoint when resuming execution. */
2410 if ( RT_SUCCESS(rc)
2411 && pEvent->enmType == DBGFEVENT_BREAKPOINT)
2412 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r eflags.rf = 1");
2413 }
2414
2415 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
2416 break;
2417 }
2418
2419 case DBGFEVENT_STEPPED:
2420 case DBGFEVENT_STEPPED_HYPER:
2421 {
2422 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
2423 break;
2424 }
2425
2426 case DBGFEVENT_ASSERTION_HYPER:
2427 {
2428 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2429 "\ndbgf event: Hypervisor Assertion! (%s)\n"
2430 "%s"
2431 "%s"
2432 "\n",
2433 dbgcGetEventCtx(pEvent->enmCtx),
2434 pEvent->u.Assert.pszMsg1,
2435 pEvent->u.Assert.pszMsg2);
2436 if (RT_SUCCESS(rc))
2437 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2438 break;
2439 }
2440
2441 case DBGFEVENT_DEV_STOP:
2442 {
2443 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2444 "\n"
2445 "dbgf event: DBGFSTOP (%s)\n"
2446 "File: %s\n"
2447 "Line: %d\n"
2448 "Function: %s\n",
2449 dbgcGetEventCtx(pEvent->enmCtx),
2450 pEvent->u.Src.pszFile,
2451 pEvent->u.Src.uLine,
2452 pEvent->u.Src.pszFunction);
2453 if (RT_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
2454 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2455 "Message: %s\n",
2456 pEvent->u.Src.pszMessage);
2457 if (RT_SUCCESS(rc))
2458 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2459 break;
2460 }
2461
2462
2463 case DBGFEVENT_INVALID_COMMAND:
2464 {
2465 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
2466 break;
2467 }
2468
2469 case DBGFEVENT_POWERING_OFF:
2470 {
2471 pThis->Dbgc.fReady = false;
2472 pThis->Dbgc.pIo->pfnSetReady(pThis->Dbgc.pIo, false);
2473 rc = VERR_GENERAL_FAILURE;
2474 break;
2475 }
2476
2477 default:
2478 {
2479 /*
2480 * Probably a generic event. Look it up to find its name.
2481 */
2482 PCDBGCSXEVT pEvtDesc = dbgcEventLookup(pEvent->enmType);
2483 if (pEvtDesc)
2484 {
2485 if (pEvtDesc->enmKind == kDbgcSxEventKind_Interrupt)
2486 {
2487 Assert(pEvtDesc->pszDesc);
2488 Assert(pEvent->u.Generic.cArgs == 1);
2489 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s no %#llx! (%s)\n",
2490 pEvtDesc->pszDesc, pEvent->u.Generic.auArgs[0], pEvtDesc->pszName);
2491 }
2492 else if (pEvtDesc->fFlags & DBGCSXEVT_F_BUGCHECK)
2493 {
2494 Assert(pEvent->u.Generic.cArgs >= 5);
2495 char szDetails[512];
2496 DBGFR3FormatBugCheck(pDbgc->pUVM, szDetails, sizeof(szDetails), pEvent->u.Generic.auArgs[0],
2497 pEvent->u.Generic.auArgs[1], pEvent->u.Generic.auArgs[2],
2498 pEvent->u.Generic.auArgs[3], pEvent->u.Generic.auArgs[4]);
2499 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s %s%s!\n%s", pEvtDesc->pszName,
2500 pEvtDesc->pszDesc ? "- " : "", pEvtDesc->pszDesc ? pEvtDesc->pszDesc : "",
2501 szDetails);
2502 }
2503 else if ( (pEvtDesc->fFlags & DBGCSXEVT_F_TAKE_ARG)
2504 || pEvent->u.Generic.cArgs > 1
2505 || ( pEvent->u.Generic.cArgs == 1
2506 && pEvent->u.Generic.auArgs[0] != 0))
2507 {
2508 if (pEvtDesc->pszDesc)
2509 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s!",
2510 pEvtDesc->pszName, pEvtDesc->pszDesc);
2511 else
2512 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s!", pEvtDesc->pszName);
2513 if (pEvent->u.Generic.cArgs <= 1)
2514 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " arg=%#llx\n", pEvent->u.Generic.auArgs[0]);
2515 else
2516 {
2517 for (uint32_t i = 0; i < pEvent->u.Generic.cArgs; i++)
2518 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " args[%u]=%#llx", i, pEvent->u.Generic.auArgs[i]);
2519 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n");
2520 }
2521 }
2522 else
2523 {
2524 if (pEvtDesc->pszDesc)
2525 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s!\n",
2526 pEvtDesc->pszName, pEvtDesc->pszDesc);
2527 else
2528 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s!\n", pEvtDesc->pszName);
2529 }
2530 }
2531 else
2532 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
2533 break;
2534 }
2535 }
2536
2537 return rc;
2538}
2539
2540
2541/**
2542 * Run the debugger console.
2543 *
2544 * @returns VBox status code.
2545 * @param pThis Pointer to the GDB stub context.
2546 */
2547int dbgcGdbStubRun(PGDBSTUBCTX pThis)
2548{
2549 /* Select the register set based on the CPU mode. */
2550 CPUMMODE enmMode = DBGCCmdHlpGetCpuMode(&pThis->Dbgc.CmdHlp);
2551 switch (enmMode)
2552 {
2553 case CPUMMODE_PROTECTED:
2554 pThis->paRegs = &g_aGdbRegs32[0];
2555 pThis->cRegs = RT_ELEMENTS(g_aGdbRegs32);
2556 break;
2557 case CPUMMODE_LONG:
2558 pThis->paRegs = &g_aGdbRegs64[0];
2559 pThis->cRegs = RT_ELEMENTS(g_aGdbRegs64);
2560 break;
2561 case CPUMMODE_REAL:
2562 default:
2563 return DBGCCmdHlpPrintf(&pThis->Dbgc.CmdHlp, "error: Invalid CPU mode %d.\n", enmMode);
2564 }
2565
2566 /*
2567 * We're ready for commands now.
2568 */
2569 pThis->Dbgc.fReady = true;
2570 pThis->Dbgc.pIo->pfnSetReady(pThis->Dbgc.pIo, true);
2571
2572 /*
2573 * Main Debugger Loop.
2574 *
2575 * This loop will either block on waiting for input or on waiting on
2576 * debug events. If we're forwarding the log we cannot wait for long
2577 * before we must flush the log.
2578 */
2579 int rc;
2580 for (;;)
2581 {
2582 rc = VERR_SEM_OUT_OF_TURN;
2583 if (pThis->Dbgc.pUVM)
2584 rc = DBGFR3QueryWaitable(pThis->Dbgc.pUVM);
2585
2586 if (RT_SUCCESS(rc))
2587 {
2588 /*
2589 * Wait for a debug event.
2590 */
2591 DBGFEVENT Event;
2592 rc = DBGFR3EventWait(pThis->Dbgc.pUVM, 32, &Event);
2593 if (RT_SUCCESS(rc))
2594 {
2595 rc = dbgcGdbStubCtxProcessEvent(pThis, &Event);
2596 if (RT_FAILURE(rc))
2597 break;
2598 }
2599 else if (rc != VERR_TIMEOUT)
2600 break;
2601
2602 /*
2603 * Check for input.
2604 */
2605 if (pThis->Dbgc.pIo->pfnInput(pThis->Dbgc.pIo, 0))
2606 {
2607 rc = dbgcGdbStubCtxRecv(pThis);
2608 if (RT_FAILURE(rc))
2609 break;
2610 }
2611 }
2612 else if (rc == VERR_SEM_OUT_OF_TURN)
2613 {
2614 /*
2615 * Wait for input.
2616 */
2617 if (pThis->Dbgc.pIo->pfnInput(pThis->Dbgc.pIo, 1000))
2618 {
2619 rc = dbgcGdbStubCtxRecv(pThis);
2620 if (RT_FAILURE(rc))
2621 break;
2622 }
2623 }
2624 else
2625 break;
2626 }
2627
2628 return rc;
2629}
2630
2631
2632/**
2633 * @copydoc DBGC::pfnOutput
2634 */
2635static DECLCALLBACK(int) dbgcOutputGdb(void *pvUser, const char *pachChars, size_t cbChars)
2636{
2637 PGDBSTUBCTX pThis = (PGDBSTUBCTX)pvUser;
2638
2639 pThis->fOutput = true;
2640 int rc = dbgcGdbStubCtxReplySendBegin(pThis);
2641 if (RT_SUCCESS(rc))
2642 {
2643 uint8_t chConOut = 'O';
2644 rc = dbgcGdbStubCtxReplySendData(pThis, &chConOut, sizeof(chConOut));
2645 if (RT_SUCCESS(rc))
2646 {
2647 /* Convert the characters to hex. */
2648 const char *pachCur = pachChars;
2649
2650 while ( cbChars
2651 && RT_SUCCESS(rc))
2652 {
2653 uint8_t achHex[512 + 1];
2654 size_t cbThisSend = RT_MIN((sizeof(achHex) - 1) / 2, cbChars); /* Each character needs two bytes. */
2655
2656 rc = dbgcGdbStubCtxEncodeBinaryAsHex(&achHex[0], cbThisSend * 2 + 1, pachCur, cbThisSend);
2657 if (RT_SUCCESS(rc))
2658 rc = dbgcGdbStubCtxReplySendData(pThis, &achHex[0], cbThisSend * 2);
2659
2660 pachCur += cbThisSend;
2661 cbChars -= cbThisSend;
2662 }
2663 }
2664
2665 dbgcGdbStubCtxReplySendEnd(pThis);
2666 }
2667
2668 return rc;
2669}
2670
2671
2672/**
2673 * Creates a GDB stub context instance with the given backend.
2674 *
2675 * @returns VBox status code.
2676 * @param ppGdbStubCtx Where to store the pointer to the GDB stub context instance on success.
2677 * @param pIo Pointer to the I/O callback table.
2678 * @param fFlags Flags controlling the behavior.
2679 */
2680static int dbgcGdbStubCtxCreate(PPGDBSTUBCTX ppGdbStubCtx, PCDBGCIO pIo, unsigned fFlags)
2681{
2682 /*
2683 * Validate input.
2684 */
2685 AssertPtrReturn(pIo, VERR_INVALID_POINTER);
2686 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
2687
2688 /*
2689 * Allocate and initialize.
2690 */
2691 PGDBSTUBCTX pThis = (PGDBSTUBCTX)RTMemAllocZ(sizeof(*pThis));
2692 if (!pThis)
2693 return VERR_NO_MEMORY;
2694
2695 dbgcInitCmdHlp(&pThis->Dbgc);
2696 /*
2697 * This is compied from the native debug console (will be used for monitor commands)
2698 * in DBGCConsole.cpp. Try to keep both functions in sync.
2699 */
2700 pThis->Dbgc.pIo = pIo;
2701 pThis->Dbgc.pfnOutput = dbgcOutputGdb;
2702 pThis->Dbgc.pvOutputUser = pThis;
2703 pThis->Dbgc.pVM = NULL;
2704 pThis->Dbgc.pUVM = NULL;
2705 pThis->Dbgc.idCpu = 0;
2706 pThis->Dbgc.hDbgAs = DBGF_AS_GLOBAL;
2707 pThis->Dbgc.pszEmulation = "CodeView/WinDbg";
2708 pThis->Dbgc.paEmulationCmds = &g_aCmdsCodeView[0];
2709 pThis->Dbgc.cEmulationCmds = g_cCmdsCodeView;
2710 pThis->Dbgc.paEmulationFuncs = &g_aFuncsCodeView[0];
2711 pThis->Dbgc.cEmulationFuncs = g_cFuncsCodeView;
2712 //pThis->Dbgc.fLog = false;
2713 pThis->Dbgc.fRegTerse = true;
2714 pThis->Dbgc.fStepTraceRegs = true;
2715 //pThis->Dbgc.cPagingHierarchyDumps = 0;
2716 //pThis->Dbgc.DisasmPos = {0};
2717 //pThis->Dbgc.SourcePos = {0};
2718 //pThis->Dbgc.DumpPos = {0};
2719 pThis->Dbgc.pLastPos = &pThis->Dbgc.DisasmPos;
2720 //pThis->Dbgc.cbDumpElement = 0;
2721 //pThis->Dbgc.cVars = 0;
2722 //pThis->Dbgc.paVars = NULL;
2723 //pThis->Dbgc.pPlugInHead = NULL;
2724 //pThis->Dbgc.pFirstBp = NULL;
2725 //pThis->Dbgc.abSearch = {0};
2726 //pThis->Dbgc.cbSearch = 0;
2727 pThis->Dbgc.cbSearchUnit = 1;
2728 pThis->Dbgc.cMaxSearchHits = 1;
2729 //pThis->Dbgc.SearchAddr = {0};
2730 //pThis->Dbgc.cbSearchRange = 0;
2731
2732 //pThis->Dbgc.uInputZero = 0;
2733 //pThis->Dbgc.iRead = 0;
2734 //pThis->Dbgc.iWrite = 0;
2735 //pThis->Dbgc.cInputLines = 0;
2736 //pThis->Dbgc.fInputOverflow = false;
2737 pThis->Dbgc.fReady = true;
2738 pThis->Dbgc.pszScratch = &pThis->Dbgc.achScratch[0];
2739 //pThis->Dbgc.iArg = 0;
2740 //pThis->Dbgc.rcOutput = 0;
2741 //pThis->Dbgc.rcCmd = 0;
2742
2743 //pThis->Dbgc.pszHistoryFile = NULL;
2744 //pThis->Dbgc.pszGlobalInitScript = NULL;
2745 //pThis->Dbgc.pszLocalInitScript = NULL;
2746
2747 dbgcEvalInit();
2748
2749 /* Init the GDB stub specific parts. */
2750 pThis->cbPktBufMax = 0;
2751 pThis->pbPktBuf = NULL;
2752 pThis->fFeatures = GDBSTUBCTX_FEATURES_F_TGT_DESC;
2753 pThis->pachTgtXmlDesc = NULL;
2754 pThis->cbTgtXmlDesc = 0;
2755 pThis->fExtendedMode = false;
2756 pThis->fOutput = false;
2757 pThis->fInThrdInfoQuery = false;
2758 RTListInit(&pThis->LstTps);
2759 dbgcGdbStubCtxReset(pThis);
2760
2761 *ppGdbStubCtx = pThis;
2762 return VINF_SUCCESS;
2763}
2764
2765
2766/**
2767 * Destroys the given GDB stub context.
2768 *
2769 * @returns nothing.
2770 * @param pThis The GDB stub context to destroy.
2771 */
2772static void dbgcGdbStubDestroy(PGDBSTUBCTX pThis)
2773{
2774 AssertPtr(pThis);
2775
2776 /* Detach from the VM. */
2777 if (pThis->Dbgc.pUVM)
2778 DBGFR3Detach(pThis->Dbgc.pUVM);
2779
2780 /* Free config strings. */
2781 RTStrFree(pThis->Dbgc.pszGlobalInitScript);
2782 pThis->Dbgc.pszGlobalInitScript = NULL;
2783 RTStrFree(pThis->Dbgc.pszLocalInitScript);
2784 pThis->Dbgc.pszLocalInitScript = NULL;
2785 RTStrFree(pThis->Dbgc.pszHistoryFile);
2786 pThis->Dbgc.pszHistoryFile = NULL;
2787
2788 /* Finally, free the instance memory. */
2789 RTMemFree(pThis);
2790}
2791
2792
2793DECL_HIDDEN_CALLBACK(int) dbgcGdbStubRunloop(PUVM pUVM, PCDBGCIO pIo, unsigned fFlags)
2794{
2795 /*
2796 * Validate input.
2797 */
2798 AssertPtrNullReturn(pUVM, VERR_INVALID_VM_HANDLE);
2799 PVM pVM = NULL;
2800 if (pUVM)
2801 {
2802 pVM = VMR3GetVM(pUVM);
2803 AssertPtrReturn(pVM, VERR_INVALID_VM_HANDLE);
2804 }
2805
2806 /*
2807 * Allocate and initialize instance data
2808 */
2809 PGDBSTUBCTX pThis;
2810 int rc = dbgcGdbStubCtxCreate(&pThis, pIo, fFlags);
2811 if (RT_FAILURE(rc))
2812 return rc;
2813 if (!HMR3IsEnabled(pUVM) && !NEMR3IsEnabled(pUVM))
2814 pThis->Dbgc.hDbgAs = DBGF_AS_RC_AND_GC_GLOBAL;
2815
2816 /*
2817 * Attach to the specified VM.
2818 */
2819 if (RT_SUCCESS(rc) && pUVM)
2820 {
2821 rc = DBGFR3Attach(pUVM);
2822 if (RT_SUCCESS(rc))
2823 {
2824 pThis->Dbgc.pVM = pVM;
2825 pThis->Dbgc.pUVM = pUVM;
2826 pThis->Dbgc.idCpu = 0;
2827 }
2828 else
2829 rc = pThis->Dbgc.CmdHlp.pfnVBoxError(&pThis->Dbgc.CmdHlp, rc, "When trying to attach to VM %p\n", pThis->Dbgc.pVM);
2830 }
2831
2832 /*
2833 * Load plugins.
2834 */
2835 if (RT_SUCCESS(rc))
2836 {
2837 if (pVM)
2838 DBGFR3PlugInLoadAll(pThis->Dbgc.pUVM);
2839 dbgcEventInit(&pThis->Dbgc);
2840 //dbgcRunInitScripts(pDbgc); Not yet
2841
2842 if (!DBGFR3IsHalted(pThis->Dbgc.pUVM, VMCPUID_ALL))
2843 rc = DBGFR3Halt(pThis->Dbgc.pUVM, VMCPUID_ALL);
2844
2845 /*
2846 * Run the debugger main loop.
2847 */
2848 rc = dbgcGdbStubRun(pThis);
2849 dbgcEventTerm(&pThis->Dbgc);
2850 }
2851
2852 /*
2853 * Cleanup console debugger session.
2854 */
2855 dbgcGdbStubDestroy(pThis);
2856 return rc == VERR_DBGC_QUIT ? VINF_SUCCESS : rc;
2857}
2858
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette