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