exts.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /*++
  2. Copyright (c) 2012 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. exts.c
  5. Abstract:
  6. This module contains support for loading and running debugger extensions.
  7. Author:
  8. Evan Green 10-Sep-2012
  9. Environment:
  10. Debug Client
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "dbgrtl.h"
  16. #include <minoca/lib/im.h>
  17. #include <minoca/debug/spproto.h>
  18. #include <minoca/debug/dbgext.h>
  19. #include "symbols.h"
  20. #include "dbgapi.h"
  21. #include "dbgrprof.h"
  22. #include "dbgrcomm.h"
  23. #include "extsp.h"
  24. #include "../dbgext/extimp.h"
  25. #include <errno.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. //
  30. // ---------------------------------------------------------------- Definitions
  31. //
  32. //
  33. // ------------------------------------------------------ Data Type Definitions
  34. //
  35. typedef struct _DEBUGGER_EXTENSION {
  36. LIST_ENTRY ListEntry;
  37. PSTR BinaryName;
  38. ULONG Handle;
  39. LIST_ENTRY ExtensionsHead;
  40. } DEBUGGER_EXTENSION, *PDEBUGGER_EXTENSION;
  41. typedef struct _DEBUGGER_EXTENSION_ENTRY {
  42. LIST_ENTRY ListEntry;
  43. PSTR Command;
  44. PEXTENSION_PROTOTYPE Handler;
  45. PSTR OneLineDescription;
  46. } DEBUGGER_EXTENSION_ENTRY, *PDEBUGGER_EXTENSION_ENTRY;
  47. //
  48. // ----------------------------------------------- Internal Function Prototypes
  49. //
  50. PDEBUGGER_EXTENSION
  51. DbgpFindExtension (
  52. PDEBUGGER_CONTEXT Context,
  53. PSTR BinaryName
  54. );
  55. PDEBUGGER_EXTENSION_ENTRY
  56. DbgpFindExtensionEntry (
  57. PDEBUGGER_CONTEXT Context,
  58. PSTR ExtensionCommand
  59. );
  60. //
  61. // -------------------------------------------------------------------- Globals
  62. //
  63. DEBUG_EXTENSION_IMPORT_INTERFACE DbgExports = {
  64. DEBUG_EXTENSION_INTERFACE_VERSION,
  65. DbgRegisterExtension,
  66. DbgOutVaList,
  67. DbgEvaluate,
  68. DbgPrintAddressSymbol,
  69. DbgReadMemory,
  70. DbgWriteMemory,
  71. DbgReboot,
  72. DbgGetCallStack,
  73. DbgPrintCallStack,
  74. DbgGetTargetInformation,
  75. DbgGetTargetPointerSize,
  76. DbgGetMemberOffset,
  77. DbgGetTypeByName,
  78. DbgReadIntegerMember,
  79. DbgReadTypeByName,
  80. DbgReadType,
  81. DbgPrintTypeMember,
  82. };
  83. //
  84. // ------------------------------------------------------------------ Functions
  85. //
  86. INT
  87. DbgInitializeExtensions (
  88. PDEBUGGER_CONTEXT Context
  89. )
  90. /*++
  91. Routine Description:
  92. This routine initializes support for debugger extensions and loads any
  93. extensions supplied on the command line.
  94. Arguments:
  95. Context - Supplies a pointer to the application context.
  96. Return Value:
  97. 0 on success.
  98. Returns an error code on failure.
  99. --*/
  100. {
  101. INITIALIZE_LIST_HEAD(&(Context->LoadedExtensions));
  102. return 0;
  103. }
  104. INT
  105. DbgLoadExtension (
  106. PDEBUGGER_CONTEXT Context,
  107. PSTR BinaryName
  108. )
  109. /*++
  110. Routine Description:
  111. This routine loads a debugger extension library.
  112. Arguments:
  113. Context - Supplies a pointer to the application context.
  114. BinaryName - Supplies the path to the binary to load.
  115. Return Value:
  116. 0 on success.
  117. Returns an error code on failure.
  118. --*/
  119. {
  120. PDEBUGGER_EXTENSION ExistingExtension;
  121. PEXTENSION_ENTRY_INTERNAL ExtensionEntry;
  122. BOOL ExtensionLinked;
  123. ULONG Handle;
  124. PDEBUGGER_EXTENSION NewExtension;
  125. INT Result;
  126. ExtensionLinked = FALSE;
  127. NewExtension = NULL;
  128. //
  129. // Ensure a library of the same name is not already loaded.
  130. //
  131. ExistingExtension = DbgpFindExtension(Context, BinaryName);
  132. if (ExistingExtension != NULL) {
  133. Result = EEXIST;
  134. goto LoadExtensionEnd;
  135. }
  136. //
  137. // Attempt to load the library. If this fails, cleanup and exit.
  138. //
  139. Handle = DbgLoadLibrary(BinaryName);
  140. if (Handle == 0) {
  141. Result = EINVAL;
  142. goto LoadExtensionEnd;
  143. }
  144. //
  145. // Attempt to find the entrypoint. If this fails, cleanup and exit.
  146. //
  147. ExtensionEntry = DbgGetProcedureAddress(Handle, EXTENSION_ENTRY_NAME);
  148. if (ExtensionEntry == NULL) {
  149. Result = EINVAL;
  150. DbgOut("Error: Extension entry function %s could not be found.\n",
  151. EXTENSION_ENTRY_NAME);
  152. goto LoadExtensionEnd;
  153. }
  154. //
  155. // Allocate space to store the extension information and binary name.
  156. //
  157. NewExtension = MALLOC(sizeof(DEBUGGER_EXTENSION));
  158. if (NewExtension == NULL) {
  159. Result = ENOMEM;
  160. goto LoadExtensionEnd;
  161. }
  162. RtlZeroMemory(NewExtension, sizeof(DEBUGGER_EXTENSION));
  163. INITIALIZE_LIST_HEAD(&(NewExtension->ExtensionsHead));
  164. NewExtension->BinaryName = MALLOC(RtlStringLength(BinaryName) + 1);
  165. if (NewExtension->BinaryName == NULL) {
  166. Result = ENOMEM;
  167. goto LoadExtensionEnd;
  168. }
  169. RtlStringCopy(NewExtension->BinaryName,
  170. BinaryName,
  171. RtlStringLength(BinaryName) + 1);
  172. NewExtension->Handle = Handle;
  173. INSERT_BEFORE(&(NewExtension->ListEntry), &(Context->LoadedExtensions));
  174. ExtensionLinked = TRUE;
  175. //
  176. // Call the entry point and allow the extension to initialize.
  177. //
  178. Result = ExtensionEntry(EXTENSION_API_VERSION,
  179. Context,
  180. NewExtension,
  181. &DbgExports);
  182. if (Result != 0) {
  183. goto LoadExtensionEnd;
  184. }
  185. LoadExtensionEnd:
  186. if (Result != 0) {
  187. if (NewExtension != NULL) {
  188. if (ExtensionLinked != FALSE) {
  189. LIST_REMOVE(&(NewExtension->ListEntry));
  190. }
  191. if (NewExtension->BinaryName != NULL) {
  192. FREE(NewExtension->BinaryName);
  193. }
  194. FREE(NewExtension);
  195. }
  196. }
  197. return Result;
  198. }
  199. VOID
  200. DbgUnloadExtension (
  201. PDEBUGGER_CONTEXT Context,
  202. PSTR BinaryName
  203. )
  204. /*++
  205. Routine Description:
  206. This routine unloads and frees a debugger extension library.
  207. Arguments:
  208. Context - Supplies a pointer to the application context.
  209. BinaryName - Supplies the path to the binary to unload.
  210. Return Value:
  211. None.
  212. --*/
  213. {
  214. PLIST_ENTRY CurrentEntry;
  215. PDEBUGGER_EXTENSION Extension;
  216. PDEBUGGER_EXTENSION_ENTRY ExtensionEntry;
  217. //
  218. // Attempt to find the extension.
  219. //
  220. Extension = DbgpFindExtension(Context, BinaryName);
  221. if (Extension == NULL) {
  222. return;
  223. }
  224. //
  225. // Free all extension entries.
  226. //
  227. CurrentEntry = Extension->ExtensionsHead.Next;
  228. while (CurrentEntry != &(Extension->ExtensionsHead)) {
  229. ExtensionEntry = LIST_VALUE(CurrentEntry,
  230. DEBUGGER_EXTENSION_ENTRY,
  231. ListEntry);
  232. CurrentEntry = CurrentEntry->Next;
  233. FREE(ExtensionEntry);
  234. }
  235. //
  236. // Unlink the extension, unload the library, and free the memory.
  237. //
  238. LIST_REMOVE(&(Extension->ListEntry));
  239. DbgFreeLibrary(Extension->Handle);
  240. FREE(Extension->BinaryName);
  241. FREE(Extension);
  242. return;
  243. }
  244. VOID
  245. DbgUnloadAllExtensions (
  246. PDEBUGGER_CONTEXT Context
  247. )
  248. /*++
  249. Routine Description:
  250. This routine unloads all debugger extensions.
  251. Arguments:
  252. Context - Supplies a pointer to the application context.
  253. Return Value:
  254. None.
  255. --*/
  256. {
  257. PDEBUGGER_EXTENSION Extension;
  258. while (LIST_EMPTY(&(Context->LoadedExtensions)) == FALSE) {
  259. Extension = LIST_VALUE(Context->LoadedExtensions.Next,
  260. DEBUGGER_EXTENSION,
  261. ListEntry);
  262. DbgUnloadExtension(Context, Extension->BinaryName);
  263. }
  264. return;
  265. }
  266. INT
  267. DbgDispatchExtension (
  268. PDEBUGGER_CONTEXT Context,
  269. PSTR *Arguments,
  270. ULONG ArgumentCount
  271. )
  272. /*++
  273. Routine Description:
  274. This routine dispatches a debugger extension command.
  275. Arguments:
  276. Context - Supplies a pointer to the application context.
  277. Arguments - Supplies an array of strings containing the arguments. The
  278. first argument is the command itself.
  279. ArgumentCount - Supplies the count of arguments. This is always at least
  280. one.
  281. Return Value:
  282. 0 on success.
  283. Returns an error code on failure.
  284. --*/
  285. {
  286. PSTR Command;
  287. PSTR CommandCopy;
  288. PLIST_ENTRY CurrentEntry;
  289. PLIST_ENTRY CurrentExtension;
  290. PDEBUGGER_EXTENSION_ENTRY CurrentExtensionEntry;
  291. PDEBUGGER_EXTENSION Extension;
  292. INT Status;
  293. PSTR SubCommand;
  294. Command = Arguments[0] + 1;
  295. //
  296. // If the command is just a !, print out all extensions with a description.
  297. //
  298. if (*Command == '\0') {
  299. //
  300. // Loop through all registered extension binaries.
  301. //
  302. CurrentExtension = Context->LoadedExtensions.Next;
  303. while (CurrentExtension != &(Context->LoadedExtensions)) {
  304. Extension = LIST_VALUE(CurrentExtension,
  305. DEBUGGER_EXTENSION,
  306. ListEntry);
  307. CurrentExtension = CurrentExtension->Next;
  308. DbgOut("%s:\n", Extension->BinaryName);
  309. //
  310. // Loop through all extensions of the current binary.
  311. //
  312. CurrentEntry = Extension->ExtensionsHead.Next;
  313. while (CurrentEntry != &(Extension->ExtensionsHead)) {
  314. CurrentExtensionEntry = LIST_VALUE(CurrentEntry,
  315. DEBUGGER_EXTENSION_ENTRY,
  316. ListEntry);
  317. CurrentEntry = CurrentEntry->Next;
  318. DbgOut(" !%s - %s\n",
  319. CurrentExtensionEntry->Command,
  320. CurrentExtensionEntry->OneLineDescription);
  321. }
  322. }
  323. Status = 0;
  324. //
  325. // Find the extension and dispatch it.
  326. //
  327. } else {
  328. //
  329. // Find the first period, which splits the extension to its subcommand.
  330. //
  331. CommandCopy = strdup(Command);
  332. if (CommandCopy == NULL) {
  333. return ENOMEM;
  334. }
  335. SubCommand = strchr(CommandCopy, '.');
  336. if (SubCommand != NULL) {
  337. *SubCommand = '\0';
  338. SubCommand += 1;
  339. }
  340. //
  341. // Find the extension and dispatch it.
  342. //
  343. CurrentExtensionEntry = DbgpFindExtensionEntry(Context, CommandCopy);
  344. if (CurrentExtensionEntry != NULL) {
  345. Status = CurrentExtensionEntry->Handler(Context,
  346. SubCommand,
  347. ArgumentCount,
  348. Arguments);
  349. } else {
  350. DbgOut("Error: Extension !%s not found.\n", Command);
  351. Status = ENOENT;
  352. }
  353. free(CommandCopy);
  354. }
  355. return Status;
  356. }
  357. INT
  358. DbgRegisterExtension (
  359. PDEBUGGER_CONTEXT Context,
  360. PVOID Token,
  361. PSTR ExtensionName,
  362. PSTR OneLineDescription,
  363. PEXTENSION_PROTOTYPE Routine
  364. )
  365. /*++
  366. Routine Description:
  367. This routine registers a debugger extension with the client.
  368. Arguments:
  369. Context - Supplies a pointer to the application context.
  370. Token - Supplies the unique token provided to the extension library upon
  371. initialization.
  372. ExtensionName - Supplies the name of the extension to register. This name
  373. must not already be registered by the current extension or any other.
  374. OneLineDescription - Supplies a quick description of the extension, no
  375. longer than 60 characters. This parameter is not optional.
  376. Routine - Supplies the routine to call when the given extension is
  377. invoked.
  378. Return Value:
  379. 0 on success.
  380. Returns an error code on failure.
  381. --*/
  382. {
  383. PDEBUGGER_EXTENSION_ENTRY ExistingEntry;
  384. PDEBUGGER_EXTENSION Extension;
  385. PDEBUGGER_EXTENSION_ENTRY NewEntry;
  386. INT Result;
  387. NewEntry = NULL;
  388. //
  389. // The token is actually just a pointer to the extension structure. Though
  390. // this is susceptible to tampering, this DLL is loaded in our address
  391. // space and has already been allowed to run arbitrary code. If it wanted
  392. // to take the process down, it could have already.
  393. //
  394. if (Token == NULL) {
  395. return EINVAL;
  396. }
  397. Extension = (PDEBUGGER_EXTENSION)Token;
  398. //
  399. // Descriptions are *not* optional.
  400. //
  401. if (OneLineDescription == NULL) {
  402. return EINVAL;
  403. }
  404. //
  405. // Refuse to register extensions that are already registered.
  406. //
  407. ExistingEntry = DbgpFindExtensionEntry(Context, ExtensionName);
  408. if (ExistingEntry != NULL) {
  409. Result = EEXIST;
  410. goto RegisterExtensionEnd;
  411. }
  412. NewEntry = MALLOC(sizeof(DEBUGGER_EXTENSION_ENTRY));
  413. if (NewEntry == NULL) {
  414. Result = ENOMEM;
  415. goto RegisterExtensionEnd;
  416. }
  417. RtlZeroMemory(NewEntry, sizeof(DEBUGGER_EXTENSION_ENTRY));
  418. NewEntry->Command = ExtensionName;
  419. NewEntry->Handler = Routine;
  420. NewEntry->OneLineDescription = OneLineDescription;
  421. INSERT_BEFORE(&(NewEntry->ListEntry), &(Extension->ExtensionsHead));
  422. Result = 0;
  423. RegisterExtensionEnd:
  424. if (Result != 0) {
  425. if (NewEntry != NULL) {
  426. FREE(NewEntry);
  427. }
  428. }
  429. return Result;
  430. }
  431. //
  432. // --------------------------------------------------------- Internal Functions
  433. //
  434. PDEBUGGER_EXTENSION
  435. DbgpFindExtension (
  436. PDEBUGGER_CONTEXT Context,
  437. PSTR BinaryName
  438. )
  439. /*++
  440. Routine Description:
  441. This routine finds a loaded debugger extension matching the given binary
  442. name.
  443. Arguments:
  444. Context - Supplies a pointer to the application context.
  445. BinaryName - Supplies the extension binary name.
  446. Return Value:
  447. Returns a pointer to the loaded extension, or NULL if the extension could
  448. not be found.
  449. --*/
  450. {
  451. PLIST_ENTRY Entry;
  452. PDEBUGGER_EXTENSION Extension;
  453. Entry = Context->LoadedExtensions.Next;
  454. while (Entry != &(Context->LoadedExtensions)) {
  455. Extension = LIST_VALUE(Entry, DEBUGGER_EXTENSION, ListEntry);
  456. if (RtlAreStringsEqual(BinaryName, Extension->BinaryName, 1024) !=
  457. FALSE) {
  458. return Extension;
  459. }
  460. }
  461. return NULL;
  462. }
  463. PDEBUGGER_EXTENSION_ENTRY
  464. DbgpFindExtensionEntry (
  465. PDEBUGGER_CONTEXT Context,
  466. PSTR ExtensionCommand
  467. )
  468. /*++
  469. Routine Description:
  470. This routine finds the extension entry corresponding to the given extension
  471. command.
  472. Arguments:
  473. Context - Supplies a pointer to the application context.
  474. ExtensionCommand - Supplies the extension command, not including the
  475. leading !.
  476. Return Value:
  477. Returns a pointer to the extension entry, or NULL if none was registered.
  478. --*/
  479. {
  480. PLIST_ENTRY CurrentEntry;
  481. PLIST_ENTRY CurrentExtension;
  482. PDEBUGGER_EXTENSION_ENTRY CurrentExtensionEntry;
  483. PDEBUGGER_EXTENSION Extension;
  484. BOOL StringsEqual;
  485. //
  486. // Loop through all registered extension binaries.
  487. //
  488. CurrentExtension = Context->LoadedExtensions.Next;
  489. while (CurrentExtension != &(Context->LoadedExtensions)) {
  490. Extension = LIST_VALUE(CurrentExtension, DEBUGGER_EXTENSION, ListEntry);
  491. CurrentExtension = CurrentExtension->Next;
  492. //
  493. // Loop through all extensions of the current binary.
  494. //
  495. CurrentEntry = Extension->ExtensionsHead.Next;
  496. while (CurrentEntry != &(Extension->ExtensionsHead)) {
  497. CurrentExtensionEntry = LIST_VALUE(CurrentEntry,
  498. DEBUGGER_EXTENSION_ENTRY,
  499. ListEntry);
  500. CurrentEntry = CurrentEntry->Next;
  501. StringsEqual = RtlAreStringsEqual(CurrentExtensionEntry->Command,
  502. ExtensionCommand,
  503. MAX_EXTENSION_COMMAND);
  504. if (StringsEqual != FALSE) {
  505. return CurrentExtensionEntry;
  506. }
  507. }
  508. }
  509. return NULL;
  510. }