VirtualBox

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

Last change on this file was 103433, checked in by vboxsync, 2 months ago

Debugger: Some warning fixes, bugref:3409

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