[TASKMGR] Process page: Allow using "Open File Location" functionality without runnin...
[reactos.git] / ntoskrnl / kdbg / kdb_symbols.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/kdbg/kdb_symbols.c
5 * PURPOSE: Getting symbol information...
6 *
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
8 * Colin Finck (colin@reactos.org)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #include "kdb.h"
15
16 #define NDEBUG
17 #include "debug.h"
18
19 /* GLOBALS ******************************************************************/
20
21 typedef struct _IMAGE_SYMBOL_INFO_CACHE
22 {
23 LIST_ENTRY ListEntry;
24 ULONG RefCount;
25 UNICODE_STRING FileName;
26 PROSSYM_INFO RosSymInfo;
27 }
28 IMAGE_SYMBOL_INFO_CACHE, *PIMAGE_SYMBOL_INFO_CACHE;
29
30 static BOOLEAN LoadSymbols = FALSE;
31 static LIST_ENTRY SymbolsToLoad;
32 static KSPIN_LOCK SymbolsToLoadLock;
33 static KEVENT SymbolsToLoadEvent;
34
35 /* FUNCTIONS ****************************************************************/
36
37 static
38 BOOLEAN
39 KdbpSymSearchModuleList(
40 IN PLIST_ENTRY current_entry,
41 IN PLIST_ENTRY end_entry,
42 IN PLONG Count,
43 IN PVOID Address,
44 IN INT Index,
45 OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry)
46 {
47 while (current_entry && current_entry != end_entry)
48 {
49 *pLdrEntry = CONTAINING_RECORD(current_entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
50
51 if ((Address && Address >= (PVOID)(*pLdrEntry)->DllBase && Address < (PVOID)((ULONG_PTR)(*pLdrEntry)->DllBase + (*pLdrEntry)->SizeOfImage)) ||
52 (Index >= 0 && (*Count)++ == Index))
53 {
54 return TRUE;
55 }
56
57 current_entry = current_entry->Flink;
58 }
59
60 return FALSE;
61 }
62
63 /*! \brief Find a module...
64 *
65 * \param Address If \a Address is not NULL the module containing \a Address
66 * is searched.
67 * \param Name If \a Name is not NULL the module named \a Name will be
68 * searched.
69 * \param Index If \a Index is >= 0 the Index'th module will be returned.
70 * \param pLdrEntry Pointer to a PLDR_DATA_TABLE_ENTRY which is filled.
71 *
72 * \retval TRUE Module was found, \a pLdrEntry was filled.
73 * \retval FALSE No module was found.
74 */
75 BOOLEAN
76 KdbpSymFindModule(
77 IN PVOID Address OPTIONAL,
78 IN INT Index OPTIONAL,
79 OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry)
80 {
81 LONG Count = 0;
82 PEPROCESS CurrentProcess;
83
84 /* First try to look up the module in the kernel module list. */
85 KeAcquireSpinLockAtDpcLevel(&PsLoadedModuleSpinLock);
86 if(KdbpSymSearchModuleList(PsLoadedModuleList.Flink,
87 &PsLoadedModuleList,
88 &Count,
89 Address,
90 Index,
91 pLdrEntry))
92 {
93 KeReleaseSpinLockFromDpcLevel(&PsLoadedModuleSpinLock);
94 return TRUE;
95 }
96 KeReleaseSpinLockFromDpcLevel(&PsLoadedModuleSpinLock);
97
98 /* That didn't succeed. Try the module list of the current process now. */
99 CurrentProcess = PsGetCurrentProcess();
100
101 if(!CurrentProcess || !CurrentProcess->Peb || !CurrentProcess->Peb->Ldr)
102 return FALSE;
103
104 return KdbpSymSearchModuleList(CurrentProcess->Peb->Ldr->InLoadOrderModuleList.Flink,
105 &CurrentProcess->Peb->Ldr->InLoadOrderModuleList,
106 &Count,
107 Address,
108 Index,
109 pLdrEntry);
110 }
111
112 static
113 PCHAR
114 NTAPI
115 KdbpSymUnicodeToAnsi(IN PUNICODE_STRING Unicode,
116 OUT PCHAR Ansi,
117 IN ULONG Length)
118 {
119 PCHAR p;
120 PWCHAR pw;
121 ULONG i;
122
123 /* Set length and normalize it */
124 i = Unicode->Length / sizeof(WCHAR);
125 i = min(i, Length - 1);
126
127 /* Set source and destination, and copy */
128 pw = Unicode->Buffer;
129 p = Ansi;
130 while (i--) *p++ = (CHAR)*pw++;
131
132 /* Null terminate and return */
133 *p = ANSI_NULL;
134 return Ansi;
135 }
136
137 /*! \brief Print address...
138 *
139 * Tries to lookup line number, file name and function name for the given
140 * address and prints it.
141 * If no such information is found the address is printed in the format
142 * <module: offset>, otherwise the format will be
143 * <module: offset (filename:linenumber (functionname))>
144 *
145 * \retval TRUE Module containing \a Address was found, \a Address was printed.
146 * \retval FALSE No module containing \a Address was found, nothing was printed.
147 */
148 BOOLEAN
149 KdbSymPrintAddress(
150 IN PVOID Address,
151 IN PCONTEXT Context)
152 {
153 PLDR_DATA_TABLE_ENTRY LdrEntry;
154 ULONG_PTR RelativeAddress;
155 BOOLEAN Printed = FALSE;
156 CHAR ModuleNameAnsi[64];
157
158 if (!KdbpSymFindModule(Address, -1, &LdrEntry))
159 return FALSE;
160
161 RelativeAddress = (ULONG_PTR)Address - (ULONG_PTR)LdrEntry->DllBase;
162
163 KdbpSymUnicodeToAnsi(&LdrEntry->BaseDllName,
164 ModuleNameAnsi,
165 sizeof(ModuleNameAnsi));
166
167 if (LdrEntry->PatchInformation)
168 {
169 ULONG LineNumber;
170 CHAR FileName[256];
171 CHAR FunctionName[256];
172
173 if (RosSymGetAddressInformation(LdrEntry->PatchInformation,
174 RelativeAddress,
175 &LineNumber,
176 FileName,
177 FunctionName))
178 {
179 KdbPrintf("<%s:%x (%s:%d (%s))>",
180 ModuleNameAnsi, RelativeAddress,
181 FileName, LineNumber, FunctionName);
182 Printed = TRUE;
183 }
184 }
185
186 if (!Printed)
187 {
188 /* Just print module & address */
189 KdbPrintf("<%s:%x>", ModuleNameAnsi, RelativeAddress);
190 }
191
192 return TRUE;
193 }
194
195 static KSTART_ROUTINE LoadSymbolsRoutine;
196 /*! \brief The symbol loader thread routine.
197 * This opens the image file for reading and loads the symbols
198 * section from there.
199 *
200 * \note We must do this because KdbSymProcessSymbols is
201 * called at high IRQL and we can't set the event from here
202 *
203 * \param Context Unused
204 */
205 _Use_decl_annotations_
206 VOID
207 NTAPI
208 LoadSymbolsRoutine(
209 _In_ PVOID Context)
210 {
211 UNREFERENCED_PARAMETER(Context);
212
213 while (TRUE)
214 {
215 PLIST_ENTRY ListEntry;
216 NTSTATUS Status = KeWaitForSingleObject(&SymbolsToLoadEvent, WrKernel, KernelMode, FALSE, NULL);
217 if (!NT_SUCCESS(Status))
218 {
219 DPRINT1("KeWaitForSingleObject failed?! 0x%08x\n", Status);
220 LoadSymbols = FALSE;
221 return;
222 }
223
224 while ((ListEntry = ExInterlockedRemoveHeadList(&SymbolsToLoad, &SymbolsToLoadLock)))
225 {
226 PLDR_DATA_TABLE_ENTRY LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
227 HANDLE FileHandle;
228 OBJECT_ATTRIBUTES Attrib;
229 IO_STATUS_BLOCK Iosb;
230 InitializeObjectAttributes(&Attrib, &LdrEntry->FullDllName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
231 DPRINT1("Trying %wZ\n", &LdrEntry->FullDllName);
232 Status = ZwOpenFile(&FileHandle,
233 FILE_READ_ACCESS | SYNCHRONIZE,
234 &Attrib,
235 &Iosb,
236 FILE_SHARE_READ,
237 FILE_SYNCHRONOUS_IO_NONALERT);
238 if (!NT_SUCCESS(Status))
239 {
240 /* Try system paths */
241 static const UNICODE_STRING System32Dir = RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\");
242 UNICODE_STRING ImagePath;
243 WCHAR ImagePathBuffer[256];
244 RtlInitEmptyUnicodeString(&ImagePath, ImagePathBuffer, sizeof(ImagePathBuffer));
245 RtlCopyUnicodeString(&ImagePath, &System32Dir);
246 RtlAppendUnicodeStringToString(&ImagePath, &LdrEntry->BaseDllName);
247 InitializeObjectAttributes(&Attrib, &ImagePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
248 DPRINT1("Trying %wZ\n", &ImagePath);
249 Status = ZwOpenFile(&FileHandle,
250 FILE_READ_ACCESS | SYNCHRONIZE,
251 &Attrib,
252 &Iosb,
253 FILE_SHARE_READ,
254 FILE_SYNCHRONOUS_IO_NONALERT);
255 if (!NT_SUCCESS(Status))
256 {
257 static const UNICODE_STRING DriversDir= RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\drivers\\");
258
259 RtlInitEmptyUnicodeString(&ImagePath, ImagePathBuffer, sizeof(ImagePathBuffer));
260 RtlCopyUnicodeString(&ImagePath, &DriversDir);
261 RtlAppendUnicodeStringToString(&ImagePath, &LdrEntry->BaseDllName);
262 InitializeObjectAttributes(&Attrib, &ImagePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
263 DPRINT1("Trying %wZ\n", &ImagePath);
264 Status = ZwOpenFile(&FileHandle,
265 FILE_READ_ACCESS | SYNCHRONIZE,
266 &Attrib,
267 &Iosb,
268 FILE_SHARE_READ,
269 FILE_SYNCHRONOUS_IO_NONALERT);
270 }
271 }
272
273 if (!NT_SUCCESS(Status))
274 {
275 DPRINT1("Failed opening file %wZ (%wZ) for reading symbols (0x%08x)\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName, Status);
276 /* We took a ref previously */
277 MmUnloadSystemImage(LdrEntry);
278 continue;
279 }
280
281 /* Hand it to Rossym */
282 if (!RosSymCreateFromFile(&FileHandle, (PROSSYM_INFO*)&LdrEntry->PatchInformation))
283 LdrEntry->PatchInformation = NULL;
284
285 /* We're done for this one. */
286 NtClose(FileHandle);
287 MmUnloadSystemImage(LdrEntry);
288 }
289 }
290 }
291
292 /*! \brief Load symbols from image mapping. If this fails,
293 *
294 * \param LdrEntry The entry to load symbols from
295 */
296 VOID
297 KdbSymProcessSymbols(
298 _Inout_ PLDR_DATA_TABLE_ENTRY LdrEntry,
299 _In_ BOOLEAN Load)
300 {
301 if (!LoadSymbols)
302 return;
303
304 /* Check if this is unload */
305 if (!Load)
306 {
307 /* Did we process it */
308 if (LdrEntry->PatchInformation)
309 {
310 RosSymDelete(LdrEntry->PatchInformation);
311 LdrEntry->PatchInformation = NULL;
312 }
313 return;
314 }
315
316 if (RosSymCreateFromMem(LdrEntry->DllBase, LdrEntry->SizeOfImage, (PROSSYM_INFO*)&LdrEntry->PatchInformation))
317 {
318 return;
319 }
320
321 /* Add a ref until we really process it */
322 LdrEntry->LoadCount++;
323
324 /* Tell our worker thread to read from it */
325 KeAcquireSpinLockAtDpcLevel(&SymbolsToLoadLock);
326 InsertTailList(&SymbolsToLoad, &LdrEntry->InInitializationOrderLinks);
327 KeReleaseSpinLockFromDpcLevel(&SymbolsToLoadLock);
328
329 KeSetEvent(&SymbolsToLoadEvent, IO_NO_INCREMENT, FALSE);
330 }
331
332
333 /**
334 * @brief Initializes the KDB symbols implementation.
335 *
336 * @param[in] BootPhase
337 * Phase of initialization.
338 *
339 * @return
340 * TRUE if symbols are to be loaded at this given BootPhase; FALSE if not.
341 **/
342 BOOLEAN
343 KdbSymInit(
344 _In_ ULONG BootPhase)
345 {
346 #if 1 // FIXME: This is a workaround HACK!!
347 static BOOLEAN OrigLoadSymbols = FALSE;
348 #endif
349
350 DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase);
351
352 if (BootPhase == 0)
353 {
354 PSTR CommandLine;
355 SHORT Found = FALSE;
356 CHAR YesNo;
357
358 /* By default, load symbols in DBG builds, but not in REL builds
359 or anything other than x86, because they only work on x86
360 and can cause the system to hang on x64. */
361 #if DBG && defined(_M_IX86)
362 LoadSymbols = TRUE;
363 #else
364 LoadSymbols = FALSE;
365 #endif
366
367 /* Check the command line for LOADSYMBOLS, NOLOADSYMBOLS,
368 * LOADSYMBOLS={YES|NO}, NOLOADSYMBOLS={YES|NO} */
369 ASSERT(KeLoaderBlock);
370 CommandLine = KeLoaderBlock->LoadOptions;
371 while (*CommandLine)
372 {
373 /* Skip any whitespace */
374 while (isspace(*CommandLine))
375 ++CommandLine;
376
377 Found = 0;
378 if (_strnicmp(CommandLine, "LOADSYMBOLS", 11) == 0)
379 {
380 Found = +1;
381 CommandLine += 11;
382 }
383 else if (_strnicmp(CommandLine, "NOLOADSYMBOLS", 13) == 0)
384 {
385 Found = -1;
386 CommandLine += 13;
387 }
388 if (Found != 0)
389 {
390 if (*CommandLine == '=')
391 {
392 ++CommandLine;
393 YesNo = toupper(*CommandLine);
394 if (YesNo == 'N' || YesNo == '0')
395 {
396 Found = -1 * Found;
397 }
398 }
399 LoadSymbols = (0 < Found);
400 }
401
402 /* Move on to the next option */
403 while (*CommandLine && !isspace(*CommandLine))
404 ++CommandLine;
405 }
406
407 #if 1 // FIXME: This is a workaround HACK!!
408 // Save the actual value of LoadSymbols but disable it for BootPhase 0.
409 OrigLoadSymbols = LoadSymbols;
410 LoadSymbols = FALSE;
411 return OrigLoadSymbols;
412 #endif
413 }
414 else if (BootPhase == 1)
415 {
416 HANDLE Thread;
417 NTSTATUS Status;
418 KIRQL OldIrql;
419 PLIST_ENTRY ListEntry;
420
421 #if 1 // FIXME: This is a workaround HACK!!
422 // Now, restore the actual value of LoadSymbols.
423 LoadSymbols = OrigLoadSymbols;
424 #endif
425
426 /* Do not continue loading symbols if we have less than 96MB of RAM */
427 if (MmNumberOfPhysicalPages < (96 * 1024 * 1024 / PAGE_SIZE))
428 LoadSymbols = FALSE;
429
430 /* Continue this phase only if we need to load symbols */
431 if (!LoadSymbols)
432 return LoadSymbols;
433
434 /* Launch our worker thread */
435 InitializeListHead(&SymbolsToLoad);
436 KeInitializeSpinLock(&SymbolsToLoadLock);
437 KeInitializeEvent(&SymbolsToLoadEvent, SynchronizationEvent, FALSE);
438
439 Status = PsCreateSystemThread(&Thread,
440 THREAD_ALL_ACCESS,
441 NULL, NULL, NULL,
442 LoadSymbolsRoutine,
443 NULL);
444 if (!NT_SUCCESS(Status))
445 {
446 DPRINT1("Failed starting symbols loader thread: 0x%08x\n", Status);
447 LoadSymbols = FALSE;
448 return LoadSymbols;
449 }
450
451 RosSymInitKernelMode();
452
453 KeAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
454
455 for (ListEntry = PsLoadedModuleList.Flink;
456 ListEntry != &PsLoadedModuleList;
457 ListEntry = ListEntry->Flink)
458 {
459 PLDR_DATA_TABLE_ENTRY LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
460 KdbSymProcessSymbols(LdrEntry, TRUE);
461 }
462
463 KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
464 }
465
466 return LoadSymbols;
467 }
468
469 /* EOF */