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