VirtualBox

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

Last change on this file since 84691 was 84691, checked in by vboxsync, 5 years ago

Debugger/GdbRemoteStub: Implement detach request and fix querying registers [doxygen fix]

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