VirtualBox

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

Last change on this file since 97364 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use