Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

774 lines
20 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. dbgkproc.c
  5. Abstract:
  6. This module implements process control primitives for the
  7. Dbg component of NT
  8. Author:
  9. Mark Lucovsky (markl) 19-Jan-1990
  10. Revision History:
  11. --*/
  12. #include "dbgkp.h"
  13. #ifdef ALLOC_PRAGMA
  14. #pragma alloc_text(PAGE, DbgkpSuspendProcess)
  15. #pragma alloc_text(PAGE, DbgkpResumeProcess)
  16. #pragma alloc_text(PAGE, DbgkpSectionToFileHandle)
  17. #pragma alloc_text(PAGE, DbgkCreateThread)
  18. #pragma alloc_text(PAGE, DbgkExitThread)
  19. #pragma alloc_text(PAGE, DbgkExitProcess)
  20. #pragma alloc_text(PAGE, DbgkMapViewOfSection)
  21. #pragma alloc_text(PAGE, DbgkUnMapViewOfSection)
  22. #endif
  23. BOOLEAN
  24. DbgkpSuspendProcess (
  25. VOID
  26. )
  27. /*++
  28. Routine Description:
  29. This function causes all threads in the calling process except for
  30. the calling thread to suspend.
  31. Arguments:
  32. CreateDeleteLockHeld - Supplies a flag that specifies whether or not
  33. the caller is holding the process create delete lock. If the
  34. caller holds the lock, than this function will not aquire the
  35. lock before suspending the process.
  36. Return Value:
  37. None.
  38. --*/
  39. {
  40. PAGED_CODE();
  41. //
  42. // Freeze the execution of all threads in the current process, but
  43. // the calling thread. If we are in the process of being deleted don't do this.
  44. //
  45. if ((PsGetCurrentProcess()->Flags&PS_PROCESS_FLAGS_PROCESS_DELETE) == 0) {
  46. KeFreezeAllThreads();
  47. return TRUE;
  48. }
  49. return FALSE;
  50. }
  51. VOID
  52. DbgkpResumeProcess (
  53. VOID
  54. )
  55. /*++
  56. Routine Description:
  57. This function causes all threads in the calling process except for
  58. the calling thread to resume.
  59. Arguments:
  60. CreateDeleteLockHeld - Supplies a flag that specifies whether or not
  61. the caller is holding the process create delete lock. If the
  62. caller holds the lock, than this function will not aquire the
  63. lock before suspending the process.
  64. Return Value:
  65. None.
  66. --*/
  67. {
  68. PAGED_CODE();
  69. //
  70. // Thaw the execution of all threads in the current process, but
  71. // the calling thread.
  72. //
  73. KeThawAllThreads();
  74. return;
  75. }
  76. HANDLE
  77. DbgkpSectionToFileHandle(
  78. IN PVOID SectionObject
  79. )
  80. /*++
  81. Routine Description:
  82. This function Opens a handle to the file associated with the processes
  83. section. The file is opened such that it can be dupped all the way to
  84. the UI where the UI can either map the file or read the file to get
  85. the debug info.
  86. Arguments:
  87. SectionHandle - Supplies a handle to the section whose associated file
  88. is to be opened.
  89. Return Value:
  90. NULL - The file could not be opened.
  91. NON-NULL - Returns a handle to the file associated with the specified
  92. section.
  93. --*/
  94. {
  95. NTSTATUS Status;
  96. ANSI_STRING FileName;
  97. UNICODE_STRING UnicodeFileName;
  98. OBJECT_ATTRIBUTES Obja;
  99. IO_STATUS_BLOCK IoStatusBlock;
  100. HANDLE Handle;
  101. PAGED_CODE();
  102. Status = MmGetFileNameForSection(SectionObject, (PSTRING)&FileName);
  103. if ( !NT_SUCCESS(Status) ) {
  104. return NULL;
  105. }
  106. Status = RtlAnsiStringToUnicodeString(&UnicodeFileName,&FileName,TRUE);
  107. ExFreePool(FileName.Buffer);
  108. if ( !NT_SUCCESS(Status) ) {
  109. return NULL;
  110. }
  111. InitializeObjectAttributes(
  112. &Obja,
  113. &UnicodeFileName,
  114. OBJ_CASE_INSENSITIVE | OBJ_FORCE_ACCESS_CHECK | OBJ_KERNEL_HANDLE,
  115. NULL,
  116. NULL
  117. );
  118. Status = ZwOpenFile(
  119. &Handle,
  120. (ACCESS_MASK)(GENERIC_READ | SYNCHRONIZE),
  121. &Obja,
  122. &IoStatusBlock,
  123. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  124. FILE_SYNCHRONOUS_IO_NONALERT
  125. );
  126. RtlFreeUnicodeString(&UnicodeFileName);
  127. if ( !NT_SUCCESS(Status) ) {
  128. return NULL;
  129. } else {
  130. return Handle;
  131. }
  132. }
  133. VOID
  134. DbgkCreateThread(
  135. PETHREAD Thread,
  136. PVOID StartAddress
  137. )
  138. /*++
  139. Routine Description:
  140. This function is called when a new thread begins to execute. If the
  141. thread has an associated DebugPort, then a message is sent thru the
  142. port.
  143. If this thread is the first thread in the process, then this event
  144. is translated into a CreateProcessInfo message.
  145. If a message is sent, then while the thread is awaiting a reply,
  146. all other threads in the process are suspended.
  147. Arguments:
  148. Thread - New thread just being started
  149. StartAddress - Supplies the start address for the thread that is
  150. starting.
  151. Return Value:
  152. None.
  153. --*/
  154. {
  155. PVOID Port;
  156. DBGKM_APIMSG m;
  157. PDBGKM_CREATE_THREAD CreateThreadArgs;
  158. PDBGKM_CREATE_PROCESS CreateProcessArgs;
  159. PEPROCESS Process;
  160. PDBGKM_LOAD_DLL LoadDllArgs;
  161. NTSTATUS Status;
  162. OBJECT_ATTRIBUTES Obja;
  163. IO_STATUS_BLOCK IoStatusBlock;
  164. PIMAGE_NT_HEADERS NtHeaders;
  165. PTEB Teb;
  166. ULONG OldFlags;
  167. #if defined(_WIN64)
  168. PVOID Wow64Process;
  169. #endif
  170. PAGED_CODE();
  171. Process = PsGetCurrentProcessByThread (Thread);
  172. #if defined(_WIN64)
  173. Wow64Process = Process->Wow64Process;
  174. #endif
  175. OldFlags = PS_TEST_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_CREATE_REPORTED|PS_PROCESS_FLAGS_IMAGE_NOTIFY_DONE);
  176. if ((OldFlags&PS_PROCESS_FLAGS_IMAGE_NOTIFY_DONE) == 0 && PsImageNotifyEnabled) {
  177. IMAGE_INFO ImageInfo;
  178. ANSI_STRING FileName;
  179. UNICODE_STRING UnicodeFileName;
  180. PUNICODE_STRING pUnicodeFileName;
  181. //
  182. // notification of main .exe
  183. //
  184. ImageInfo.Properties = 0;
  185. ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
  186. ImageInfo.ImageBase = Process->SectionBaseAddress;
  187. ImageInfo.ImageSize = 0;
  188. try {
  189. NtHeaders = RtlImageNtHeader (Process->SectionBaseAddress);
  190. if (NtHeaders) {
  191. #if defined(_WIN64)
  192. if (Wow64Process != NULL) {
  193. ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER ((PIMAGE_NT_HEADERS32)NtHeaders, SizeOfImage);
  194. } else {
  195. #endif
  196. ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER (NtHeaders, SizeOfImage);
  197. #if defined(_WIN64)
  198. }
  199. #endif
  200. }
  201. } except (EXCEPTION_EXECUTE_HANDLER) {
  202. ImageInfo.ImageSize = 0;
  203. }
  204. ImageInfo.ImageSelector = 0;
  205. ImageInfo.ImageSectionNumber = 0;
  206. pUnicodeFileName = NULL;
  207. Status = MmGetFileNameForSection (Process->SectionObject, (PSTRING)&FileName);
  208. if (NT_SUCCESS (Status)) {
  209. Status = RtlAnsiStringToUnicodeString (&UnicodeFileName, &FileName,TRUE);
  210. ExFreePool (FileName.Buffer);
  211. if (NT_SUCCESS (Status)) {
  212. pUnicodeFileName = &UnicodeFileName;
  213. }
  214. }
  215. PsCallImageNotifyRoutines (pUnicodeFileName,
  216. Process->UniqueProcessId,
  217. &ImageInfo);
  218. if (pUnicodeFileName != NULL) {
  219. RtlFreeUnicodeString (pUnicodeFileName);
  220. }
  221. //
  222. // and of ntdll.dll
  223. //
  224. ImageInfo.Properties = 0;
  225. ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
  226. ImageInfo.ImageBase = PsSystemDllBase;
  227. ImageInfo.ImageSize = 0;
  228. try {
  229. NtHeaders = RtlImageNtHeader (PsSystemDllBase);
  230. if ( NtHeaders ) {
  231. #if defined(_WIN64)
  232. if (Wow64Process != NULL) {
  233. ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER ((PIMAGE_NT_HEADERS32)NtHeaders, SizeOfImage);
  234. } else {
  235. #endif
  236. ImageInfo.ImageSize = DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER (NtHeaders, SizeOfImage);
  237. #if defined(_WIN64)
  238. }
  239. #endif
  240. }
  241. } except(EXCEPTION_EXECUTE_HANDLER) {
  242. ImageInfo.ImageSize = 0;
  243. }
  244. ImageInfo.ImageSelector = 0;
  245. ImageInfo.ImageSectionNumber = 0;
  246. RtlInitUnicodeString (&UnicodeFileName,
  247. L"\\SystemRoot\\System32\\ntdll.dll");
  248. PsCallImageNotifyRoutines (&UnicodeFileName,
  249. Process->UniqueProcessId,
  250. &ImageInfo);
  251. }
  252. Port = Process->DebugPort;
  253. if (Port == NULL) {
  254. return;
  255. }
  256. //
  257. // Make sure we only get one create process message
  258. //
  259. if ((OldFlags&PS_PROCESS_FLAGS_CREATE_REPORTED) == 0) {
  260. //
  261. // This is a create process
  262. //
  263. CreateThreadArgs = &m.u.CreateProcessInfo.InitialThread;
  264. CreateThreadArgs->SubSystemKey = 0;
  265. CreateProcessArgs = &m.u.CreateProcessInfo;
  266. CreateProcessArgs->SubSystemKey = 0;
  267. CreateProcessArgs->FileHandle = DbgkpSectionToFileHandle(
  268. Process->SectionObject
  269. );
  270. CreateProcessArgs->BaseOfImage = Process->SectionBaseAddress;
  271. CreateThreadArgs->StartAddress = NULL;
  272. CreateProcessArgs->DebugInfoFileOffset = 0;
  273. CreateProcessArgs->DebugInfoSize = 0;
  274. try {
  275. NtHeaders = RtlImageNtHeader(Process->SectionBaseAddress);
  276. if ( NtHeaders ) {
  277. #if defined(_WIN64)
  278. if (Wow64Process != NULL) {
  279. CreateThreadArgs->StartAddress = UlongToPtr (DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER ((PIMAGE_NT_HEADERS32)NtHeaders, ImageBase) +
  280. DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER ((PIMAGE_NT_HEADERS32)NtHeaders, AddressOfEntryPoint));
  281. } else {
  282. #endif
  283. CreateThreadArgs->StartAddress = (PVOID) (DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER (NtHeaders, ImageBase) +
  284. DBGKP_FIELD_FROM_IMAGE_OPTIONAL_HEADER (NtHeaders, AddressOfEntryPoint));
  285. #if defined(_WIN64)
  286. }
  287. #endif
  288. //
  289. // The following fields are safe for Wow64 as the offsets are the same for a PE32+
  290. // as a PE32 header.
  291. //
  292. CreateProcessArgs->DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
  293. CreateProcessArgs->DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
  294. }
  295. } except (EXCEPTION_EXECUTE_HANDLER) {
  296. CreateThreadArgs->StartAddress = NULL;
  297. CreateProcessArgs->DebugInfoFileOffset = 0;
  298. CreateProcessArgs->DebugInfoSize = 0;
  299. }
  300. DBGKM_FORMAT_API_MSG(m,DbgKmCreateProcessApi,sizeof(*CreateProcessArgs));
  301. DbgkpSendApiMessage(&m,FALSE);
  302. if (CreateProcessArgs->FileHandle != NULL) {
  303. ObCloseHandle(CreateProcessArgs->FileHandle, KernelMode);
  304. }
  305. LoadDllArgs = &m.u.LoadDll;
  306. LoadDllArgs->BaseOfDll = PsSystemDllBase;
  307. LoadDllArgs->DebugInfoFileOffset = 0;
  308. LoadDllArgs->DebugInfoSize = 0;
  309. LoadDllArgs->NamePointer = NULL;
  310. Teb = NULL;
  311. try {
  312. NtHeaders = RtlImageNtHeader(PsSystemDllBase);
  313. if ( NtHeaders ) {
  314. LoadDllArgs->DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
  315. LoadDllArgs->DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
  316. }
  317. //
  318. // Normaly the ntdll loaded fills in this pointer for the debug API's. We fake it here
  319. // as ntdll isn't loaded yet and it can't load itself.
  320. //
  321. Teb = Thread->Tcb.Teb;
  322. if (Teb != NULL) {
  323. wcsncpy (Teb->StaticUnicodeBuffer,
  324. L"ntdll.dll",
  325. sizeof (Teb->StaticUnicodeBuffer) / sizeof (Teb->StaticUnicodeBuffer[0]));
  326. Teb->NtTib.ArbitraryUserPointer = Teb->StaticUnicodeBuffer;
  327. LoadDllArgs->NamePointer = &Teb->NtTib.ArbitraryUserPointer;
  328. }
  329. } except (EXCEPTION_EXECUTE_HANDLER) {
  330. LoadDllArgs->DebugInfoFileOffset = 0;
  331. LoadDllArgs->DebugInfoSize = 0;
  332. LoadDllArgs->NamePointer = NULL;
  333. }
  334. //
  335. // Send load dll section for NT dll !
  336. //
  337. InitializeObjectAttributes(
  338. &Obja,
  339. (PUNICODE_STRING)&PsNtDllPathName,
  340. OBJ_CASE_INSENSITIVE | OBJ_FORCE_ACCESS_CHECK | OBJ_KERNEL_HANDLE,
  341. NULL,
  342. NULL
  343. );
  344. Status = ZwOpenFile(
  345. &LoadDllArgs->FileHandle,
  346. (ACCESS_MASK)(GENERIC_READ | SYNCHRONIZE),
  347. &Obja,
  348. &IoStatusBlock,
  349. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  350. FILE_SYNCHRONOUS_IO_NONALERT
  351. );
  352. if (!NT_SUCCESS (Status)) {
  353. LoadDllArgs->FileHandle = NULL;
  354. }
  355. DBGKM_FORMAT_API_MSG(m,DbgKmLoadDllApi,sizeof(*LoadDllArgs));
  356. DbgkpSendApiMessage(&m,TRUE);
  357. if (LoadDllArgs->FileHandle != NULL) {
  358. ObCloseHandle(LoadDllArgs->FileHandle, KernelMode);
  359. }
  360. try {
  361. if (Teb != NULL) {
  362. Teb->NtTib.ArbitraryUserPointer = NULL;
  363. }
  364. } except (EXCEPTION_EXECUTE_HANDLER) {
  365. }
  366. } else {
  367. CreateThreadArgs = &m.u.CreateThread;
  368. CreateThreadArgs->SubSystemKey = 0;
  369. CreateThreadArgs->StartAddress = StartAddress;
  370. DBGKM_FORMAT_API_MSG (m,DbgKmCreateThreadApi,sizeof(*CreateThreadArgs));
  371. DbgkpSendApiMessage (&m,TRUE);
  372. }
  373. }
  374. VOID
  375. DbgkExitThread(
  376. NTSTATUS ExitStatus
  377. )
  378. /*++
  379. Routine Description:
  380. This function is called when a new thread terminates. At this
  381. point, the thread will no longer execute in user-mode. No other
  382. exit processing has occured.
  383. If a message is sent, then while the thread is awaiting a reply,
  384. all other threads in the process are suspended.
  385. Arguments:
  386. ExitStatus - Supplies the ExitStatus of the exiting thread.
  387. Return Value:
  388. None.
  389. --*/
  390. {
  391. PVOID Port;
  392. DBGKM_APIMSG m;
  393. PDBGKM_EXIT_THREAD args;
  394. PEPROCESS Process;
  395. BOOLEAN Frozen;
  396. PAGED_CODE();
  397. Process = PsGetCurrentProcess();
  398. if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
  399. Port = NULL;
  400. } else {
  401. Port = Process->DebugPort;
  402. }
  403. if ( !Port ) {
  404. return;
  405. }
  406. if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) {
  407. return;
  408. }
  409. args = &m.u.ExitThread;
  410. args->ExitStatus = ExitStatus;
  411. DBGKM_FORMAT_API_MSG(m,DbgKmExitThreadApi,sizeof(*args));
  412. Frozen = DbgkpSuspendProcess();
  413. DbgkpSendApiMessage(&m,FALSE);
  414. if (Frozen) {
  415. DbgkpResumeProcess();
  416. }
  417. }
  418. VOID
  419. DbgkExitProcess(
  420. NTSTATUS ExitStatus
  421. )
  422. /*++
  423. Routine Description:
  424. This function is called when a process terminates. The address
  425. space of the process is still intact, but no threads exist in
  426. the process.
  427. Arguments:
  428. ExitStatus - Supplies the ExitStatus of the exiting process.
  429. Return Value:
  430. None.
  431. --*/
  432. {
  433. PVOID Port;
  434. DBGKM_APIMSG m;
  435. PDBGKM_EXIT_PROCESS args;
  436. PEPROCESS Process;
  437. PAGED_CODE();
  438. Process = PsGetCurrentProcess();
  439. if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
  440. Port = NULL;
  441. } else {
  442. Port = Process->DebugPort;
  443. }
  444. if ( !Port ) {
  445. return;
  446. }
  447. if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) {
  448. return;
  449. }
  450. //
  451. // this ensures that other timed lockers of the process will bail
  452. // since this call is done while holding the process lock, and lock duration
  453. // is controlled by debugger
  454. //
  455. KeQuerySystemTime(&PsGetCurrentProcess()->ExitTime);
  456. args = &m.u.ExitProcess;
  457. args->ExitStatus = ExitStatus;
  458. DBGKM_FORMAT_API_MSG(m,DbgKmExitProcessApi,sizeof(*args));
  459. DbgkpSendApiMessage(&m,FALSE);
  460. }
  461. VOID
  462. DbgkMapViewOfSection(
  463. IN PVOID SectionObject,
  464. IN PVOID BaseAddress,
  465. IN ULONG SectionOffset,
  466. IN ULONG_PTR ViewSize
  467. )
  468. /*++
  469. Routine Description:
  470. This function is called when the current process successfully
  471. maps a view of an image section. If the process has an associated
  472. debug port, then a load dll message is sent.
  473. Arguments:
  474. SectionObject - Supplies a pointer to the section mapped by the
  475. process.
  476. BaseAddress - Supplies the base address of where the section is
  477. mapped in the current process address space.
  478. SectionOffset - Supplies the offset in the section where the
  479. processes mapped view begins.
  480. ViewSize - Supplies the size of the mapped view.
  481. Return Value:
  482. None.
  483. --*/
  484. {
  485. PVOID Port;
  486. DBGKM_APIMSG m;
  487. PDBGKM_LOAD_DLL LoadDllArgs;
  488. PEPROCESS Process;
  489. PIMAGE_NT_HEADERS NtHeaders;
  490. PAGED_CODE();
  491. UNREFERENCED_PARAMETER (SectionOffset);
  492. UNREFERENCED_PARAMETER (ViewSize);
  493. if ( KeGetPreviousMode() == KernelMode ) {
  494. return;
  495. }
  496. Process = PsGetCurrentProcess();
  497. if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
  498. Port = NULL;
  499. } else {
  500. Port = Process->DebugPort;
  501. }
  502. if ( !Port ) {
  503. return;
  504. }
  505. LoadDllArgs = &m.u.LoadDll;
  506. LoadDllArgs->FileHandle = DbgkpSectionToFileHandle(SectionObject);
  507. LoadDllArgs->BaseOfDll = BaseAddress;
  508. LoadDllArgs->DebugInfoFileOffset = 0;
  509. LoadDllArgs->DebugInfoSize = 0;
  510. //
  511. // The loader fills in the module name in this pointer before mapping the section.
  512. // Its a very poor linkage.
  513. //
  514. LoadDllArgs->NamePointer = &NtCurrentTeb()->NtTib.ArbitraryUserPointer;
  515. try {
  516. NtHeaders = RtlImageNtHeader (BaseAddress);
  517. if (NtHeaders != NULL) {
  518. LoadDllArgs->DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
  519. LoadDllArgs->DebugInfoSize = NtHeaders->FileHeader.NumberOfSymbols;
  520. }
  521. } except (EXCEPTION_EXECUTE_HANDLER) {
  522. LoadDllArgs->DebugInfoFileOffset = 0;
  523. LoadDllArgs->DebugInfoSize = 0;
  524. LoadDllArgs->NamePointer = NULL;
  525. }
  526. DBGKM_FORMAT_API_MSG(m,DbgKmLoadDllApi,sizeof(*LoadDllArgs));
  527. DbgkpSendApiMessage(&m,TRUE);
  528. if (LoadDllArgs->FileHandle != NULL) {
  529. ObCloseHandle(LoadDllArgs->FileHandle, KernelMode);
  530. }
  531. }
  532. VOID
  533. DbgkUnMapViewOfSection(
  534. IN PVOID BaseAddress
  535. )
  536. /*++
  537. Routine Description:
  538. This function is called when the current process successfully
  539. un maps a view of an image section. If the process has an associated
  540. debug port, then an "unmap view of section" message is sent.
  541. Arguments:
  542. BaseAddress - Supplies the base address of the section being
  543. unmapped.
  544. Return Value:
  545. None.
  546. --*/
  547. {
  548. PVOID Port;
  549. DBGKM_APIMSG m;
  550. PDBGKM_UNLOAD_DLL UnloadDllArgs;
  551. PEPROCESS Process;
  552. PAGED_CODE();
  553. Process = PsGetCurrentProcess();
  554. if ( KeGetPreviousMode() == KernelMode ) {
  555. return;
  556. }
  557. if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
  558. Port = NULL;
  559. } else {
  560. Port = Process->DebugPort;
  561. }
  562. if ( !Port ) {
  563. return;
  564. }
  565. UnloadDllArgs = &m.u.UnloadDll;
  566. UnloadDllArgs->BaseAddress = BaseAddress;
  567. DBGKM_FORMAT_API_MSG(m,DbgKmUnloadDllApi,sizeof(*UnloadDllArgs));
  568. DbgkpSendApiMessage(&m,TRUE);
  569. }