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