VirtualBox

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

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

Debugger: Initial port of my GDB stub library from https://github.com/AlexanderEichner/libgdbstub to VirtualBox, bugref:5217

Very basic port with quite a few rough edges, this is basically a backup of the current code.

Working:

  • Halt and Resume VM execution
  • Reading registers
  • Reading/Writing memory
  • Basic support for breakpoints (removing doesn't work yet)

Still todo:

  • Cleanup, cleanup, cleanup
  • Writing memory
  • SMP support (by abusing the thread support of GDB)
  • Access to the native debugger console through the Rcmd/monitor protocol
  • More register sets (MMX,SSE,AVX, etc.)

If you want to try it out enable the normal TCP debugger interface and set the following extradata key:

VBoxInternal/DBGC/GdbStub 1

No complaints about the code at this point please, hints and comments welcome though!

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