VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFR3PlugIn.cpp@ 96860

Last change on this file since 96860 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: 20.4 KB
Line 
1/* $Id: DBGFR3PlugIn.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Plug-In Support.
4 */
5
6/*
7 * Copyright (C) 2008-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#define LOG_GROUP LOG_GROUP_DBGF
33#include <VBox/vmm/dbgf.h>
34#include <VBox/vmm/mm.h>
35#include <VBox/vmm/vmm.h>
36#include "DBGFInternal.h"
37#include <VBox/vmm/uvm.h>
38#include <VBox/vmm/vm.h>
39#include <VBox/err.h>
40#include <VBox/log.h>
41#include <VBox/version.h>
42
43#include <iprt/alloca.h>
44#include <iprt/assert.h>
45#include <iprt/ctype.h>
46#include <iprt/env.h>
47#include <iprt/dir.h>
48#include <iprt/ldr.h>
49#include <iprt/param.h>
50#include <iprt/path.h>
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56
57#define DBGF_PLUG_IN_READ_LOCK(pUVM) \
58 do { int rcLock = RTCritSectRwEnterShared(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
59#define DBGF_PLUG_IN_READ_UNLOCK(pUVM) \
60 do { int rcLock = RTCritSectRwLeaveShared(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
61
62#define DBGF_PLUG_IN_WRITE_LOCK(pUVM) \
63 do { int rcLock = RTCritSectRwEnterExcl(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
64#define DBGF_PLUG_IN_WRITE_UNLOCK(pUVM) \
65 do { int rcLock = RTCritSectRwLeaveExcl(&pUVM->dbgf.s.CritSect); AssertRC(rcLock); } while (0)
66
67/** Max allowed length of a plug-in name (excludes the path and suffix). */
68#define DBGFPLUGIN_MAX_NAME 64
69
70
71/*********************************************************************************************************************************
72* Structures and Typedefs *
73*********************************************************************************************************************************/
74/**
75 * Plug-in tracking record.
76 */
77typedef struct DBGFPLUGIN
78{
79 /** Pointer to the next plug-in. */
80 struct DBGFPLUGIN *pNext;
81 /** The loader handle. */
82 RTLDRMOD hLdrMod;
83 /** The plug-in entry point. */
84 PFNDBGFPLUGIN pfnEntry;
85 /** The name length. */
86 uint8_t cchName;
87 /** The plug-in name (variable length). */
88 char szName[1];
89} DBGFPLUGIN;
90/** Pointer to plug-in tracking record. */
91typedef DBGFPLUGIN *PDBGFPLUGIN;
92
93
94/*********************************************************************************************************************************
95* Internal Functions *
96*********************************************************************************************************************************/
97static DECLCALLBACK(void) dbgfPlugInUnloadAll(PUVM pUVM);
98static FNDBGFHANDLERINT dbgfR3PlugInInfoList;
99
100
101/**
102 * Internal init routine called by DBGFR3Init().
103 *
104 * @returns VBox status code.
105 * @param pUVM The user mode VM handle.
106 */
107int dbgfR3PlugInInit(PUVM pUVM)
108{
109 return DBGFR3InfoRegisterInternal(pUVM->pVM, "plugins", "Lists the debugger plug-ins.", dbgfR3PlugInInfoList);
110}
111
112
113/**
114 * Internal cleanup routine called by DBGFR3Term().
115 *
116 * @param pUVM The user mode VM handle.
117 */
118void dbgfR3PlugInTerm(PUVM pUVM)
119{
120 dbgfPlugInUnloadAll(pUVM);
121}
122
123
124/**
125 * Extracts the plug-in name from a plug-in specifier that may or may not
126 * include path and/or suffix.
127 *
128 * @returns VBox status code.
129 *
130 * @param pszDst Where to return the name. At least DBGFPLUGIN_MAX_NAME
131 * worth of buffer space.
132 * @param pszPlugIn The plug-in module specifier to parse.
133 * @param pErrInfo Optional error information structure.
134 */
135static int dbgfPlugInExtractName(char *pszDst, const char *pszPlugIn, PRTERRINFO pErrInfo)
136{
137 /*
138 * Parse out the name stopping at the extension.
139 */
140 const char *pszName = RTPathFilename(pszPlugIn);
141 if (!pszName || !*pszName)
142 return VERR_INVALID_NAME;
143 if (!RTStrNICmp(pszName, RT_STR_TUPLE(DBGF_PLUG_IN_PREFIX)))
144 {
145 pszName += sizeof(DBGF_PLUG_IN_PREFIX) - 1;
146 if (!*pszName)
147 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME, "Invalid plug-in name: nothing after the prefix");
148 }
149
150 int ch;
151 size_t cchName = 0;
152 while ( (ch = pszName[cchName]) != '\0'
153 && ch != '.')
154 {
155 if ( RT_C_IS_ALPHA(ch)
156 || (RT_C_IS_DIGIT(ch) && cchName != 0))
157 cchName++;
158 else
159 {
160 if (!RT_C_IS_DIGIT(ch))
161 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME, "Invalid plug-in name: '%c' is not alphanumeric", ch);
162 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME,
163 "Invalid plug-in name: Cannot start with a digit (after the prefix)");
164 }
165 }
166
167 if (cchName >= DBGFPLUGIN_MAX_NAME)
168 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME, "Invalid plug-in name: too long (max %u)", DBGFPLUGIN_MAX_NAME);
169
170 /*
171 * We're very picky about the extension when present.
172 */
173 if ( ch == '.'
174 && RTStrICmp(&pszName[cchName], RTLdrGetSuff()))
175 return RTErrInfoSetF(pErrInfo, VERR_INVALID_NAME,
176 "Invalid plug-in name: Suffix isn't the default dll/so/dylib one (%s): '%s'",
177 RTLdrGetSuff(), &pszName[cchName]);
178
179 /*
180 * Copy it.
181 */
182 memcpy(pszDst, pszName, cchName);
183 pszDst[cchName] = '\0';
184 return VINF_SUCCESS;
185}
186
187
188/**
189 * Locate a loaded plug-in.
190 *
191 * @returns Pointer to the plug-in tracking structure.
192 * @param pUVM Pointer to the user-mode VM structure.
193 * @param pszName The name of the plug-in we're looking for.
194 * @param ppPrev Where to optionally return the pointer to the
195 * previous list member.
196 */
197static PDBGFPLUGIN dbgfR3PlugInLocate(PUVM pUVM, const char *pszName, PDBGFPLUGIN *ppPrev)
198{
199 PDBGFPLUGIN pPrev = NULL;
200 PDBGFPLUGIN pCur = pUVM->dbgf.s.pPlugInHead;
201 while (pCur)
202 {
203 if (!RTStrICmp(pCur->szName, pszName))
204 {
205 if (ppPrev)
206 *ppPrev = pPrev;
207 return pCur;
208 }
209
210 /* advance */
211 pPrev = pCur;
212 pCur = pCur->pNext;
213 }
214 return NULL;
215}
216
217
218/**
219 * Try load the specified plug-in module.
220 *
221 * @returns VINF_SUCCESS on success, path error or loader error on failure.
222 *
223 * @param pPlugIn The plug-in tracing record.
224 * @param pszModule Module name.
225 * @param pErrInfo Optional error information structure.
226 */
227static int dbgfR3PlugInTryLoad(PDBGFPLUGIN pPlugIn, const char *pszModule, PRTERRINFO pErrInfo)
228{
229 /*
230 * Load it and try resolve the entry point.
231 */
232 int rc = SUPR3HardenedVerifyPlugIn(pszModule, pErrInfo);
233 if (RT_SUCCESS(rc))
234 rc = RTLdrLoadEx(pszModule, &pPlugIn->hLdrMod, RTLDRLOAD_FLAGS_LOCAL, pErrInfo);
235 if (RT_SUCCESS(rc))
236 {
237 rc = RTLdrGetSymbol(pPlugIn->hLdrMod, DBGF_PLUG_IN_ENTRYPOINT, (void **)&pPlugIn->pfnEntry);
238 if (RT_SUCCESS(rc))
239 {
240 LogRel(("DBGF: Loaded Plug-In '%s' (%s)\n", pPlugIn->szName, pszModule));
241 return VINF_SUCCESS;
242 }
243
244 RTErrInfoSet(pErrInfo, rc, "Failed to locate plug-in entrypoint (" DBGF_PLUG_IN_ENTRYPOINT ")" );
245 LogRel(("DBGF: RTLdrGetSymbol('%s', '%s',) -> %Rrc\n", pszModule, DBGF_PLUG_IN_ENTRYPOINT, rc));
246
247 RTLdrClose(pPlugIn->hLdrMod);
248 pPlugIn->hLdrMod = NIL_RTLDRMOD;
249 }
250 return rc;
251}
252
253
254/**
255 * RTPathTraverseList callback.
256 *
257 * @returns See FNRTPATHTRAVERSER.
258 *
259 * @param pchPath See FNRTPATHTRAVERSER.
260 * @param cchPath See FNRTPATHTRAVERSER.
261 * @param pvUser1 The plug-in specifier.
262 * @param pvUser2 The plug-in tracking record.
263 */
264static DECLCALLBACK(int) dbgfR3PlugInLoadCallback(const char *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
265{
266 PDBGFPLUGIN pPlugIn = (PDBGFPLUGIN)pvUser1;
267 PRTERRINFO pErrInfo = (PRTERRINFO)pvUser2;
268
269 /*
270 * Join the path and the specified plug-in name, adding prefix and suffix.
271 */
272 const char *pszSuff = RTLdrGetSuff();
273 size_t const cchSuff = strlen(pszSuff);
274 size_t const cchModule = cchPath + sizeof(RTPATH_SLASH_STR) + sizeof(DBGF_PLUG_IN_PREFIX) + pPlugIn->cchName + cchSuff + 4;
275 char *pszModule = (char *)alloca(cchModule);
276 AssertReturn(pszModule, VERR_TRY_AGAIN);
277 memcpy(pszModule, pchPath, cchPath);
278 pszModule[cchPath] = '\0';
279
280 int rc = RTPathAppend(pszModule, cchModule, DBGF_PLUG_IN_PREFIX);
281 AssertRCReturn(rc, VERR_TRY_AGAIN);
282 strcat(&pszModule[cchPath], pPlugIn->szName);
283 strcat(&pszModule[cchPath + sizeof(DBGF_PLUG_IN_PREFIX) - 1 + pPlugIn->cchName], pszSuff);
284 Assert(strlen(pszModule) < cchModule - 4);
285
286 if (RTPathExists(pszModule))
287 {
288 rc = dbgfR3PlugInTryLoad(pPlugIn, pszModule, pErrInfo);
289 if (RT_SUCCESS(rc))
290 return VINF_SUCCESS;
291 }
292
293 return VERR_TRY_AGAIN;
294}
295
296
297/**
298 * Loads a plug-in.
299 *
300 * @returns VBox status code.
301 * @param pUVM Pointer to the user-mode VM structure.
302 * @param pszName The plug-in name.
303 * @param pszMaybeModule Path to the plug-in, or just the
304 * plug-in name as specified by the user. Ignored
305 * if no path.
306 * @param pErrInfo Optional error information structure.
307 */
308static DECLCALLBACK(int) dbgfR3PlugInLoad(PUVM pUVM, const char *pszName, const char *pszMaybeModule, PRTERRINFO pErrInfo)
309{
310 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
311
312 /*
313 * Check if a plug-in by the given name already exists.
314 */
315 PDBGFPLUGIN pPlugIn = dbgfR3PlugInLocate(pUVM, pszName, NULL);
316 if (pPlugIn)
317 {
318 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
319 return RTErrInfoSetF(pErrInfo, VERR_ALREADY_EXISTS, "A plug-in by the name '%s' already exists", pszName);
320 }
321
322 /*
323 * Create a module structure and we can pass around via RTPathTraverseList if needed.
324 */
325 size_t cbName = strlen(pszName) + 1;
326 pPlugIn = (PDBGFPLUGIN)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF, RT_UOFFSETOF_DYN(DBGFPLUGIN, szName[cbName]));
327 if (RT_UNLIKELY(!pPlugIn))
328 {
329 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
330 return VERR_NO_MEMORY;
331 }
332 memcpy(pPlugIn->szName, pszName, cbName);
333 pPlugIn->cchName = (uint8_t)cbName - 1;
334 Assert(pPlugIn->cchName == cbName - 1);
335
336 /*
337 * If the caller specified a path, try load exactly what was specified.
338 */
339 int rc;
340 if (RTPathHavePath(pszMaybeModule))
341 rc = dbgfR3PlugInTryLoad(pPlugIn, pszMaybeModule, pErrInfo);
342 else
343 {
344 /*
345 * No path specified, search for the plug-in using the canonical
346 * module name for it.
347 */
348 RTErrInfoClear(pErrInfo);
349
350 /* 1. The private architecture directory. */
351 char szPath[_4K];
352 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
353 if (RT_SUCCESS(rc))
354 rc = RTPathTraverseList(szPath, '\0', dbgfR3PlugInLoadCallback, pPlugIn, pErrInfo);
355 if (RT_FAILURE_NP(rc))
356 {
357 /* 2. The config value 'PlugInPath' */
358 int rc2 = CFGMR3QueryString(CFGMR3GetChild(CFGMR3GetRootU(pUVM), "/DBGF"), "PlugInPath", szPath, sizeof(szPath));
359 if (RT_SUCCESS(rc2))
360 rc = RTPathTraverseList(szPath, ';', dbgfR3PlugInLoadCallback, pPlugIn, pErrInfo);
361 if (RT_FAILURE_NP(rc))
362 {
363 /* 3. The VBOXDBG_PLUG_IN_PATH environment variable. */
364 rc2 = RTEnvGetEx(RTENV_DEFAULT, "VBOXDBG_PLUG_IN_PATH", szPath, sizeof(szPath), NULL);
365 if (RT_SUCCESS(rc2))
366 rc = RTPathTraverseList(szPath, ';', dbgfR3PlugInLoadCallback, pPlugIn, pErrInfo);
367 }
368 }
369
370 if (rc == VERR_END_OF_STRING)
371 rc = VERR_FILE_NOT_FOUND;
372 if (pErrInfo && !RTErrInfoIsSet(pErrInfo))
373 RTErrInfoSetF(pErrInfo, rc, "Failed to locate '%s'", pPlugIn->szName);
374 }
375 if (RT_SUCCESS(rc))
376 {
377 /*
378 * Try initialize it.
379 */
380 rc = pPlugIn->pfnEntry(DBGFPLUGINOP_INIT, pUVM, VMMR3GetVTable(), VBOX_VERSION);
381 if (RT_SUCCESS(rc))
382 {
383 /*
384 * Link it and we're good.
385 */
386 pPlugIn->pNext = pUVM->dbgf.s.pPlugInHead;
387 pUVM->dbgf.s.pPlugInHead = pPlugIn;
388
389 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
390 return VINF_SUCCESS;
391 }
392
393 RTErrInfoSet(pErrInfo, rc, "Plug-in init failed");
394 LogRel(("DBGF: Plug-in '%s' failed during init: %Rrc\n", pPlugIn->szName, rc));
395 RTLdrClose(pPlugIn->hLdrMod);
396 }
397 MMR3HeapFree(pPlugIn);
398
399 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
400 return rc;
401}
402
403
404/**
405 * Load a debugging plug-in.
406 *
407 * @returns VBox status code.
408 * @retval VERR_ALREADY_EXISTS if the module was already loaded.
409 * @retval VINF_BUFFER_OVERFLOW if the actual plug-in name buffer was too small
410 * (the plug-in was still successfully loaded).
411 * @param pUVM Pointer to the user-mode VM structure.
412 * @param pszPlugIn The plug-in name. This may specify the exact path to
413 * the plug-in module, or it may just specify the core name
414 * of the plug-in without prefix, suffix and path.
415 * @param pszActual Buffer to return the actual plug-in name in. Optional.
416 * This will be returned on VERR_ALREADY_EXSIST too.
417 * @param cbActual The size of @a pszActual.
418 * @param pErrInfo Optional error information structure.
419 */
420VMMR3DECL(int) DBGFR3PlugInLoad(PUVM pUVM, const char *pszPlugIn, char *pszActual, size_t cbActual, PRTERRINFO pErrInfo)
421{
422 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
423 AssertPtrReturn(pszPlugIn, VERR_INVALID_PARAMETER);
424
425 /*
426 * Extract the plug-in name. Copy it to the return buffer as we'll want to
427 * return it in the VERR_ALREADY_EXISTS case too.
428 */
429 char szName[DBGFPLUGIN_MAX_NAME];
430 int rc = dbgfPlugInExtractName(szName, pszPlugIn, pErrInfo);
431 if (RT_SUCCESS(rc))
432 {
433 int rc2 = VINF_SUCCESS;
434 if (pszActual)
435 rc2 = RTStrCopy(pszActual, cbActual, szName);
436
437 /*
438 * Write lock releated DBGF bits and try load it.
439 */
440 rc = VMR3ReqPriorityCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)dbgfR3PlugInLoad, 4, pUVM, szName, pszPlugIn, pErrInfo);
441 if (rc2 != VINF_SUCCESS && RT_SUCCESS(rc))
442 rc = VINF_BUFFER_OVERFLOW;
443 }
444
445 return rc;
446}
447
448
449/**
450 * Load all plug-ins from the architechture private directory of VBox.
451 *
452 * @param pUVM Pointer to the user-mode VM structure.
453 */
454VMMR3DECL(void) DBGFR3PlugInLoadAll(PUVM pUVM)
455{
456 UVM_ASSERT_VALID_EXT_RETURN_VOID(pUVM);
457
458 /*
459 * Pass it on to EMT(0) if necessary (thanks to DBGFR3Os*).
460 */
461 if (VMR3GetVMCPUId(pUVM->pVM) != 0)
462 {
463 VMR3ReqPriorityCallVoidWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)DBGFR3PlugInLoadAll, 1, pUVM);
464 return;
465 }
466
467
468 /*
469 * Open the architecture specific directory with a filter on our prefix
470 * and names including a dot.
471 */
472 const char *pszSuff = RTLdrGetSuff();
473 size_t cchSuff = strlen(pszSuff);
474
475 char szPath[RTPATH_MAX];
476 int rc = RTPathAppPrivateArch(szPath, sizeof(szPath) - cchSuff);
477 AssertRCReturnVoid(rc);
478 size_t offDir = strlen(szPath);
479
480 rc = RTPathAppend(szPath, sizeof(szPath) - cchSuff, DBGF_PLUG_IN_PREFIX "*");
481 AssertRCReturnVoid(rc);
482 strcat(szPath, pszSuff);
483
484 RTDIR hDir;
485 rc = RTDirOpenFiltered(&hDir, szPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
486 if (RT_SUCCESS(rc))
487 {
488 /*
489 * Now read it and try load each of the plug-in modules.
490 */
491 RTDIRENTRY DirEntry;
492 while (RT_SUCCESS(RTDirRead(hDir, &DirEntry, NULL)))
493 {
494 szPath[offDir] = '\0';
495 rc = RTPathAppend(szPath, sizeof(szPath), DirEntry.szName);
496 if (RT_SUCCESS(rc))
497 {
498 char szName[DBGFPLUGIN_MAX_NAME];
499 rc = dbgfPlugInExtractName(szName, DirEntry.szName, NULL);
500 if (RT_SUCCESS(rc))
501 {
502 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
503 dbgfR3PlugInLoad(pUVM, szName, szPath, NULL);
504 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
505 }
506 }
507 }
508
509 RTDirClose(hDir);
510 }
511}
512
513
514/**
515 * Unloads a plug-in by name (no path, prefix or suffix).
516 *
517 * @returns VBox status code.
518 * @retval VERR_NOT_FOUND if the specified plug-in wasn't found.
519 * @param pUVM Pointer to the user-mode VM structure.
520 * @param pszName The name of the plug-in to unload.
521 */
522VMMR3DECL(int) DBGFR3PlugInUnload(PUVM pUVM, const char *pszName)
523{
524 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
525
526 /*
527 * Pass it on to EMT(0) if necessary (thanks to DBGFR3Os*).
528 */
529 if (VMR3GetVMCPUId(pUVM->pVM) != 0)
530 return VMR3ReqPriorityCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)DBGFR3PlugInUnload, 2, pUVM, pszName);
531
532
533 /*
534 * Find the plug-in.
535 */
536 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
537
538 int rc;
539 PDBGFPLUGIN pPrevPlugIn;
540 PDBGFPLUGIN pPlugIn = dbgfR3PlugInLocate(pUVM, pszName, &pPrevPlugIn);
541 if (pPlugIn)
542 {
543 /*
544 * Unlink, terminate, unload and free the plug-in.
545 */
546 if (pPrevPlugIn)
547 pPrevPlugIn->pNext = pPlugIn->pNext;
548 else
549 pUVM->dbgf.s.pPlugInHead = pPlugIn->pNext;
550
551 pPlugIn->pfnEntry(DBGFPLUGINOP_TERM, pUVM, VMMR3GetVTable(), 0);
552 RTLdrClose(pPlugIn->hLdrMod);
553
554 pPlugIn->pfnEntry = NULL;
555 pPlugIn->hLdrMod = NIL_RTLDRMOD;
556 MMR3HeapFree(pPlugIn->pNext);
557 rc = VINF_SUCCESS;
558 }
559 else
560 rc = VERR_NOT_FOUND;
561
562 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
563 return rc;
564}
565
566
567/**
568 * Unload all plug-ins.
569 *
570 * @param pUVM Pointer to the user-mode VM structure.
571 */
572static DECLCALLBACK(void) dbgfPlugInUnloadAll(PUVM pUVM)
573{
574 DBGF_PLUG_IN_WRITE_LOCK(pUVM);
575
576 while (pUVM->dbgf.s.pPlugInHead)
577 {
578 PDBGFPLUGIN pPlugin = pUVM->dbgf.s.pPlugInHead;
579 pUVM->dbgf.s.pPlugInHead = pPlugin->pNext;
580
581 pPlugin->pfnEntry(DBGFPLUGINOP_TERM, pUVM, VMMR3GetVTable(), 0);
582
583 int rc2 = RTLdrClose(pPlugin->hLdrMod);
584 AssertRC(rc2);
585
586 pPlugin->pfnEntry = NULL;
587 pPlugin->hLdrMod = NIL_RTLDRMOD;
588 MMR3HeapFree(pPlugin);
589 }
590
591 DBGF_PLUG_IN_WRITE_UNLOCK(pUVM);
592}
593
594
595/**
596 * Unloads all plug-ins.
597 *
598 * @param pUVM Pointer to the user-mode VM structure.
599 */
600VMMR3DECL(void) DBGFR3PlugInUnloadAll(PUVM pUVM)
601{
602 UVM_ASSERT_VALID_EXT_RETURN_VOID(pUVM);
603 /* Thanks to DBGFR3Os, this must be done on EMT(0). */
604 VMR3ReqPriorityCallVoidWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)dbgfPlugInUnloadAll, 1, pUVM);
605}
606
607
608
609/**
610 * @callback_method_impl{FNDBGFHANDLERINT, The 'plugins' info item.}
611 */
612static DECLCALLBACK(void) dbgfR3PlugInInfoList(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
613{
614 PDBGFPLUGIN pPlugIn = pVM->pUVM->dbgf.s.pPlugInHead;
615 RT_NOREF_PV(pszArgs);
616 if (pPlugIn)
617 {
618 pHlp->pfnPrintf(pHlp, "Debugging plug-in%s: %s", pPlugIn->pNext ? "s" : "", pPlugIn->szName);
619 while ((pPlugIn = pPlugIn->pNext) != NULL)
620 pHlp->pfnPrintf(pHlp, ", %s", pPlugIn->szName);
621 pHlp->pfnPrintf(pHlp, "\n");
622
623 }
624 else
625 pHlp->pfnPrintf(pHlp, "No plug-ins loaded\n");
626}
627
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use