[CMAKE]
[reactos.git] / ntoskrnl / kdbg / kdb_symbols.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/dbg/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
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS ******************************************************************/
19
20 typedef struct _IMAGE_SYMBOL_INFO_CACHE
21 {
22 LIST_ENTRY ListEntry;
23 ULONG RefCount;
24 UNICODE_STRING FileName;
25 PROSSYM_INFO RosSymInfo;
26 }
27 IMAGE_SYMBOL_INFO_CACHE, *PIMAGE_SYMBOL_INFO_CACHE;
28
29 static BOOLEAN LoadSymbols;
30 static LIST_ENTRY SymbolFileListHead;
31 static KSPIN_LOCK SymbolFileListLock;
32 static PROSSYM_INFO KdbpRosSymInfo;
33 static ULONG_PTR KdbpImageBase;
34 BOOLEAN KdbpSymbolsInitialized = FALSE;
35
36 /* FUNCTIONS ****************************************************************/
37
38 static 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 LPCWSTR Name,
45 IN INT Index,
46 OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry)
47 {
48 while (current_entry && current_entry != end_entry)
49 {
50 *pLdrEntry = CONTAINING_RECORD(current_entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
51
52 if ((Address && Address >= (PVOID)(*pLdrEntry)->DllBase && Address < (PVOID)((ULONG_PTR)(*pLdrEntry)->DllBase + (*pLdrEntry)->SizeOfImage)) ||
53 (Name && !_wcsnicmp((*pLdrEntry)->BaseDllName.Buffer, Name, (*pLdrEntry)->BaseDllName.Length / sizeof(WCHAR))) ||
54 (Index >= 0 && (*Count)++ == Index))
55 {
56 return TRUE;
57 }
58
59 current_entry = current_entry->Flink;
60 }
61
62 return FALSE;
63 }
64
65 /*! \brief Find a module...
66 *
67 * \param Address If \a Address is not NULL the module containing \a Address
68 * is searched.
69 * \param Name If \a Name is not NULL the module named \a Name will be
70 * searched.
71 * \param Index If \a Index is >= 0 the Index'th module will be returned.
72 * \param pLdrEntry Pointer to a PLDR_DATA_TABLE_ENTRY which is filled.
73 *
74 * \retval TRUE Module was found, \a pLdrEntry was filled.
75 * \retval FALSE No module was found.
76 */
77 BOOLEAN
78 KdbpSymFindModule(
79 IN PVOID Address OPTIONAL,
80 IN LPCWSTR Name OPTIONAL,
81 IN INT Index OPTIONAL,
82 OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry)
83 {
84 LONG Count = 0;
85 PEPROCESS CurrentProcess;
86
87 /* First try to look up the module in the kernel module list. */
88 if(KdbpSymSearchModuleList(PsLoadedModuleList.Flink,
89 &PsLoadedModuleList,
90 &Count,
91 Address,
92 Name,
93 Index,
94 pLdrEntry))
95 {
96 return TRUE;
97 }
98
99 /* That didn't succeed. Try the module list of the current process now. */
100 CurrentProcess = PsGetCurrentProcess();
101
102 if(!CurrentProcess || !CurrentProcess->Peb || !CurrentProcess->Peb->Ldr)
103 return FALSE;
104
105 return KdbpSymSearchModuleList(CurrentProcess->Peb->Ldr->InLoadOrderModuleList.Flink,
106 &CurrentProcess->Peb->Ldr->InLoadOrderModuleList,
107 &Count,
108 Address,
109 Name,
110 Index,
111 pLdrEntry);
112 }
113
114 /*! \brief Print address...
115 *
116 * Tries to lookup line number, file name and function name for the given
117 * address and prints it.
118 * If no such information is found the address is printed in the format
119 * <module: offset>, otherwise the format will be
120 * <module: offset (filename:linenumber (functionname))>
121 *
122 * \retval TRUE Module containing \a Address was found, \a Address was printed.
123 * \retval FALSE No module containing \a Address was found, nothing was printed.
124 */
125 BOOLEAN
126 KdbSymPrintAddress(
127 IN PVOID Address)
128 {
129 PMEMORY_AREA MemoryArea = NULL;
130 HANDLE FileHandle = NULL;
131 PROS_SECTION_OBJECT SectionObject;
132 PLDR_DATA_TABLE_ENTRY LdrEntry;
133 OBJECT_ATTRIBUTES ObjectAttributes;
134 IO_STATUS_BLOCK IoStatusBlock;
135 UNICODE_STRING ModuleFileName;
136 ULONG_PTR RelativeAddress;
137 NTSTATUS Status;
138 ULONG LineNumber;
139 CHAR FileName[256];
140 CHAR FunctionName[256];
141
142 if (!KdbpSymbolsInitialized || !KdbpSymFindModule(Address, NULL, -1, &LdrEntry))
143 return FALSE;
144
145 RelativeAddress = (ULONG_PTR)Address - (ULONG_PTR)LdrEntry->DllBase;
146 Status = KdbSymGetAddressInformation(LdrEntry->PatchInformation,
147 RelativeAddress,
148 &LineNumber,
149 FileName,
150 FunctionName);
151 if (NT_SUCCESS(Status))
152 {
153 DbgPrint("<%wZ:%x (%s:%d (%s))>",
154 &LdrEntry->BaseDllName, RelativeAddress, FileName, LineNumber, FunctionName);
155 return TRUE;
156 }
157 else if (Address < MmSystemRangeStart)
158 {
159 MemoryArea = MmLocateMemoryAreaByAddress(&PsGetCurrentProcess()->Vm, Address);
160 if (!MemoryArea || MemoryArea->Type != MEMORY_AREA_SECTION_VIEW)
161 {
162 goto end;
163 }
164 SectionObject = MemoryArea->Data.SectionData.Section;
165 if (!(SectionObject->AllocationAttributes & SEC_IMAGE)) goto end;
166 if (SectionObject->ImageSection->ImageBase != KdbpImageBase)
167 {
168 if (KdbpRosSymInfo)
169 {
170 RosSymDelete(KdbpRosSymInfo);
171 KdbpRosSymInfo = NULL;
172 }
173
174 Status = MmGetFileNameForAddress(Address, &ModuleFileName);
175 if (!NT_SUCCESS(Status))
176 goto end;
177
178 InitializeObjectAttributes
179 (&ObjectAttributes,
180 &ModuleFileName,
181 OBJ_CASE_INSENSITIVE,
182 NULL,
183 NULL);
184
185 if (!NT_SUCCESS
186 (ZwOpenFile
187 (&FileHandle,
188 FILE_READ_ACCESS,
189 &ObjectAttributes,
190 &IoStatusBlock,
191 FILE_SHARE_READ,
192 FILE_SYNCHRONOUS_IO_NONALERT)))
193 {
194 goto end;
195 }
196
197 if (!RosSymCreateFromFile(&FileHandle, &KdbpRosSymInfo))
198 {
199 KdbpRosSymInfo = NULL;
200 }
201
202 ZwClose(FileHandle);
203 KdbpImageBase = SectionObject->ImageSection->ImageBase;
204 }
205
206 if (KdbpRosSymInfo)
207 {
208 RelativeAddress = (ULONG_PTR)Address - KdbpImageBase;
209 Status = KdbSymGetAddressInformation
210 (KdbpRosSymInfo,
211 RelativeAddress,
212 &LineNumber,
213 FileName,
214 FunctionName);
215 if (NT_SUCCESS(Status))
216 {
217 DbgPrint
218 ("<%wZ:%x (%s:%d (%s))>",
219 &SectionObject->FileObject->FileName,
220 RelativeAddress, FileName, LineNumber, FunctionName);
221 return TRUE;
222 }
223 }
224 }
225
226 end:
227 DbgPrint("<%wZ:%x>", &LdrEntry->BaseDllName, RelativeAddress);
228
229 return TRUE;
230 }
231
232
233 /*! \brief Get information for an address (source file, line number,
234 * function name)
235 *
236 * \param SymbolInfo Pointer to ROSSYM_INFO.
237 * \param RelativeAddress Relative address to look up.
238 * \param LineNumber Pointer to an ULONG which is filled with the line
239 * number (can be NULL)
240 * \param FileName Pointer to an array of CHARs which gets filled with
241 * the filename (can be NULL)
242 * \param FunctionName Pointer to an array of CHARs which gets filled with
243 * the function name (can be NULL)
244 *
245 * \returns NTSTATUS error code.
246 * \retval STATUS_SUCCESS At least one of the requested informations was found.
247 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
248 */
249 NTSTATUS
250 KdbSymGetAddressInformation(
251 IN PROSSYM_INFO RosSymInfo,
252 IN ULONG_PTR RelativeAddress,
253 OUT PULONG LineNumber OPTIONAL,
254 OUT PCH FileName OPTIONAL,
255 OUT PCH FunctionName OPTIONAL)
256 {
257 if (!KdbpSymbolsInitialized ||
258 !RosSymInfo ||
259 !RosSymGetAddressInformation(RosSymInfo, RelativeAddress, LineNumber, FileName, FunctionName))
260 {
261 return STATUS_UNSUCCESSFUL;
262 }
263
264 return STATUS_SUCCESS;
265 }
266
267 /*! \brief Find cached symbol file.
268 *
269 * Looks through the list of cached symbol files and tries to find an already
270 * loaded one.
271 *
272 * \param FileName FileName of the symbol file to look for.
273 *
274 * \returns A pointer to the cached symbol info.
275 * \retval NULL No cached info found.
276 *
277 * \sa KdbpSymAddCachedFile
278 */
279 static PROSSYM_INFO
280 KdbpSymFindCachedFile(
281 IN PUNICODE_STRING FileName)
282 {
283 PIMAGE_SYMBOL_INFO_CACHE Current;
284 PLIST_ENTRY CurrentEntry;
285 KIRQL Irql;
286
287 KeAcquireSpinLock(&SymbolFileListLock, &Irql);
288
289 CurrentEntry = SymbolFileListHead.Flink;
290 while (CurrentEntry != (&SymbolFileListHead))
291 {
292 Current = CONTAINING_RECORD(CurrentEntry, IMAGE_SYMBOL_INFO_CACHE, ListEntry);
293
294 if (RtlEqualUnicodeString(&Current->FileName, FileName, TRUE))
295 {
296 Current->RefCount++;
297 KeReleaseSpinLock(&SymbolFileListLock, Irql);
298 DPRINT("Found cached file!\n");
299 return Current->RosSymInfo;
300 }
301
302 CurrentEntry = CurrentEntry->Flink;
303 }
304
305 KeReleaseSpinLock(&SymbolFileListLock, Irql);
306
307 DPRINT("Cached file not found!\n");
308 return NULL;
309 }
310
311 /*! \brief Add a symbol file to the cache.
312 *
313 * \param FileName Filename of the symbol file.
314 * \param RosSymInfo Pointer to the symbol info.
315 *
316 * \sa KdbpSymRemoveCachedFile
317 */
318 static VOID
319 KdbpSymAddCachedFile(
320 IN PUNICODE_STRING FileName,
321 IN PROSSYM_INFO RosSymInfo)
322 {
323 PIMAGE_SYMBOL_INFO_CACHE CacheEntry;
324
325 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo);
326
327 /* allocate entry */
328 CacheEntry = ExAllocatePoolWithTag(NonPagedPool, sizeof (IMAGE_SYMBOL_INFO_CACHE), TAG_KDBS);
329 ASSERT(CacheEntry);
330 RtlZeroMemory(CacheEntry, sizeof (IMAGE_SYMBOL_INFO_CACHE));
331
332 /* fill entry */
333 CacheEntry->FileName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
334 FileName->Length,
335 TAG_KDBS);
336 RtlCopyUnicodeString(&CacheEntry->FileName, FileName);
337 ASSERT(CacheEntry->FileName.Buffer);
338 CacheEntry->RefCount = 1;
339 CacheEntry->RosSymInfo = RosSymInfo;
340 InsertTailList(&SymbolFileListHead, &CacheEntry->ListEntry); /* FIXME: Lock list? */
341 }
342
343 /*! \brief Remove a symbol file (reference) from the cache.
344 *
345 * Tries to find a cache entry matching the given symbol info and decreases
346 * it's reference count. If the refcount is 0 after decreasing it the cache
347 * entry will be removed from the list and freed.
348 *
349 * \param RosSymInfo Pointer to the symbol info.
350 *
351 * \sa KdbpSymAddCachedFile
352 */
353 static VOID
354 KdbpSymRemoveCachedFile(
355 IN PROSSYM_INFO RosSymInfo)
356 {
357 PIMAGE_SYMBOL_INFO_CACHE Current;
358 PLIST_ENTRY CurrentEntry;
359 KIRQL Irql;
360
361 KeAcquireSpinLock(&SymbolFileListLock, &Irql);
362
363 CurrentEntry = SymbolFileListHead.Flink;
364 while (CurrentEntry != (&SymbolFileListHead))
365 {
366 Current = CONTAINING_RECORD(CurrentEntry, IMAGE_SYMBOL_INFO_CACHE, ListEntry);
367
368 if (Current->RosSymInfo == RosSymInfo) /* found */
369 {
370 ASSERT(Current->RefCount > 0);
371 Current->RefCount--;
372 if (Current->RefCount < 1)
373 {
374 RemoveEntryList(&Current->ListEntry);
375 RosSymDelete(Current->RosSymInfo);
376 ExFreePool(Current);
377 }
378
379 KeReleaseSpinLock(&SymbolFileListLock, Irql);
380 return;
381 }
382
383 CurrentEntry = CurrentEntry->Flink;
384 }
385
386 KeReleaseSpinLock(&SymbolFileListLock, Irql);
387 }
388
389 /*! \brief Loads a symbol file.
390 *
391 * \param FileName Filename of the symbol file to load.
392 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
393 *
394 * \sa KdbpSymUnloadModuleSymbols
395 */
396 static VOID
397 KdbpSymLoadModuleSymbols(
398 IN PUNICODE_STRING FileName,
399 OUT PROSSYM_INFO *RosSymInfo)
400 {
401 OBJECT_ATTRIBUTES ObjectAttributes;
402 HANDLE FileHandle;
403 NTSTATUS Status;
404 IO_STATUS_BLOCK IoStatusBlock;
405
406 /* Allow KDB to break on module load */
407 KdbModuleLoaded(FileName);
408
409 if (!LoadSymbols)
410 {
411 *RosSymInfo = NULL;
412 return;
413 }
414
415 /* Try to find cached (already loaded) symbol file */
416 *RosSymInfo = KdbpSymFindCachedFile(FileName);
417 if (*RosSymInfo)
418 {
419 DPRINT("Found cached symbol file %wZ\n", FileName);
420 return;
421 }
422
423 /* Open the file */
424 InitializeObjectAttributes(&ObjectAttributes,
425 FileName,
426 0,
427 NULL,
428 NULL);
429
430 DPRINT("Attempting to open image: %wZ\n", FileName);
431
432 Status = ZwOpenFile(&FileHandle,
433 FILE_READ_ACCESS,
434 &ObjectAttributes,
435 &IoStatusBlock,
436 FILE_SHARE_READ|FILE_SHARE_WRITE,
437 FILE_SYNCHRONOUS_IO_NONALERT);
438 if (!NT_SUCCESS(Status))
439 {
440 DPRINT("Could not open image file: %wZ\n", FileName);
441 return;
442 }
443
444 DPRINT("Loading symbols from %wZ...\n", FileName);
445
446 if (!RosSymCreateFromFile(&FileHandle, RosSymInfo))
447 {
448 DPRINT("Failed to load symbols from %wZ\n", FileName);
449 return;
450 }
451
452 ZwClose(FileHandle);
453
454 DPRINT("Symbols loaded.\n");
455
456 /* add file to cache */
457 KdbpSymAddCachedFile(FileName, *RosSymInfo);
458
459 DPRINT("Installed symbols: %wZ %p\n", FileName, *RosSymInfo);
460 }
461
462 VOID
463 KdbSymProcessSymbols(
464 IN PLDR_DATA_TABLE_ENTRY LdrEntry)
465 {
466 if (!LoadSymbols)
467 {
468 LdrEntry->PatchInformation = NULL;
469 return;
470 }
471
472 /* Remove symbol info if it already exists */
473 if (LdrEntry->PatchInformation)
474 KdbpSymRemoveCachedFile(LdrEntry->PatchInformation);
475
476 /* Error loading symbol info, try to load it from file */
477 KdbpSymLoadModuleSymbols(&LdrEntry->FullDllName,
478 (PROSSYM_INFO*)&LdrEntry->PatchInformation);
479
480 /* It already added symbols to cache */
481 DPRINT("Installed symbols: %wZ@%p-%p %p\n",
482 &LdrEntry->BaseDllName,
483 LdrEntry->DllBase,
484 (PVOID)(LdrEntry->SizeOfImage + (ULONG_PTR)LdrEntry->DllBase),
485 LdrEntry->PatchInformation);
486 }
487
488 VOID
489 NTAPI
490 KdbDebugPrint(
491 PCH Message,
492 ULONG Length)
493 {
494 /* Nothing here */
495 }
496
497
498 /*! \brief Initializes the KDB symbols implementation.
499 *
500 * \param DispatchTable Pointer to the KD dispatch table
501 * \param BootPhase Phase of initialization
502 */
503 VOID
504 NTAPI
505 KdbInitialize(
506 PKD_DISPATCH_TABLE DispatchTable,
507 ULONG BootPhase)
508 {
509 PCHAR p1, p2;
510 SHORT Found = FALSE;
511 CHAR YesNo;
512 PLDR_DATA_TABLE_ENTRY LdrEntry;
513
514 DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase);
515
516 LoadSymbols = FALSE;
517
518 #if DBG
519 /* Load symbols only if we have 96Mb of RAM or more */
520 if (MmNumberOfPhysicalPages >= 0x6000)
521 LoadSymbols = TRUE;
522 #endif
523
524 if (BootPhase == 0)
525 {
526 /* Write out the functions that we support for now */
527 DispatchTable->KdpInitRoutine = KdpKdbgInit;
528 DispatchTable->KdpPrintRoutine = KdbDebugPrint;
529
530 /* Register as a Provider */
531 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
532
533 /* Perform actual initialization of symbol module */
534 //NtoskrnlModuleObject->PatchInformation = NULL;
535 //LdrHalModuleObject->PatchInformation = NULL;
536
537 InitializeListHead(&SymbolFileListHead);
538 KeInitializeSpinLock(&SymbolFileListLock);
539
540 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
541 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
542 ASSERT(KeLoaderBlock);
543 p1 = KeLoaderBlock->LoadOptions;
544 while('\0' != *p1 && NULL != (p2 = strchr(p1, '/')))
545 {
546 p2++;
547 Found = 0;
548 if (0 == _strnicmp(p2, "LOADSYMBOLS", 11))
549 {
550 Found = +1;
551 p2 += 11;
552 }
553 else if (0 == _strnicmp(p2, "NOLOADSYMBOLS", 13))
554 {
555 Found = -1;
556 p2 += 13;
557 }
558 if (0 != Found)
559 {
560 while (isspace(*p2))
561 {
562 p2++;
563 }
564 if ('=' == *p2)
565 {
566 p2++;
567 while (isspace(*p2))
568 {
569 p2++;
570 }
571 YesNo = toupper(*p2);
572 if ('N' == YesNo || 'F' == YesNo || '0' == YesNo)
573 {
574 Found = -1 * Found;
575 }
576 }
577 LoadSymbols = (0 < Found);
578 }
579 p1 = p2;
580 }
581
582 RosSymInitKernelMode();
583 }
584 else if (BootPhase == 3)
585 {
586 /* Load symbols for NTOSKRNL.EXE.
587 It is always the first module in PsLoadedModuleList. KeLoaderBlock can't be used here as its content is just temporary. */
588 LdrEntry = CONTAINING_RECORD(PsLoadedModuleList.Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
589 KdbSymProcessSymbols(LdrEntry);
590
591 /* Also load them for HAL.DLL. */
592 LdrEntry = CONTAINING_RECORD(PsLoadedModuleList.Flink->Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
593 KdbSymProcessSymbols(LdrEntry);
594
595 KdbpSymbolsInitialized = TRUE;
596 }
597 }
598
599 /* EOF */