exts.c 16 KB

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