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...
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
10 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS ******************************************************************/
19 typedef struct _IMAGE_SYMBOL_INFO_CACHE
{
22 UNICODE_STRING FileName
;
23 PROSSYM_INFO RosSymInfo
;
24 } IMAGE_SYMBOL_INFO_CACHE
, *PIMAGE_SYMBOL_INFO_CACHE
;
26 static BOOLEAN LoadSymbols
;
27 static LIST_ENTRY SymbolFileListHead
;
28 static KSPIN_LOCK SymbolFileListLock
;
31 /* FUNCTIONS ****************************************************************/
33 /*! \brief Find a user-mode module...
35 * \param Address If \a Address is not NULL the module containing \a Address
37 * \param Name If \a Name is not NULL the module named \a Name will be
39 * \param Index If \a Index is >= 0 the Index'th module will be returned.
40 * \param pInfo Pointer to a KDB_MODULE_INFO which is filled.
42 * \retval TRUE Module was found, \a pInfo was filled.
43 * \retval FALSE No module was found.
45 * \sa KdbpSymFindModule
48 KdbpSymFindUserModule(IN PVOID Address OPTIONAL
,
49 IN LPCWSTR Name OPTIONAL
,
50 IN INT Index OPTIONAL
,
51 OUT PKDB_MODULE_INFO pInfo
)
53 PLIST_ENTRY current_entry
;
54 PLDR_DATA_TABLE_ENTRY current
;
55 PEPROCESS CurrentProcess
;
60 CurrentProcess
= PsGetCurrentProcess();
61 if (CurrentProcess
!= NULL
)
63 Peb
= CurrentProcess
->Peb
;
66 if (Peb
== NULL
|| Peb
->Ldr
== NULL
)
71 current_entry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
73 while (current_entry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
74 current_entry
!= NULL
)
76 current
= CONTAINING_RECORD(current_entry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderModuleList
);
77 Length
= min(current
->BaseDllName
.Length
/ sizeof(WCHAR
), 255);
78 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->DllBase
&&
79 Address
< (PVOID
)((char *)current
->DllBase
+ current
->SizeOfImage
))) ||
80 (Name
!= NULL
&& _wcsnicmp(current
->BaseDllName
.Buffer
, Name
, Length
) == 0) ||
81 (Index
>= 0 && Count
++ == Index
))
83 wcsncpy(pInfo
->Name
, current
->BaseDllName
.Buffer
, Length
);
84 pInfo
->Name
[Length
] = L
'\0';
85 pInfo
->Base
= (ULONG_PTR
)current
->DllBase
;
86 pInfo
->Size
= current
->SizeOfImage
;
87 pInfo
->RosSymInfo
= current
->PatchInformation
;
90 current_entry
= current_entry
->Flink
;
96 /*! \brief Find a kernel-mode module...
98 * Works like \a KdbpSymFindUserModule.
100 * \sa KdbpSymFindUserModule
103 KdbpSymFindModule(IN PVOID Address OPTIONAL
,
104 IN LPCWSTR Name OPTIONAL
,
105 IN INT Index OPTIONAL
,
106 OUT PKDB_MODULE_INFO pInfo
)
108 PLIST_ENTRY current_entry
;
109 PLDR_DATA_TABLE_ENTRY current
;
110 extern LIST_ENTRY ModuleListHead
;
114 current_entry
= ModuleListHead
.Flink
;
116 while (current_entry
!= &ModuleListHead
)
118 current
= CONTAINING_RECORD(current_entry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderModuleList
);
120 Length
= min(current
->BaseDllName
.Length
/ sizeof(WCHAR
), 255);
121 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->DllBase
&&
122 Address
< (PVOID
)((ULONG_PTR
)current
->DllBase
+ current
->SizeOfImage
))) ||
123 (Name
!= NULL
&& _wcsnicmp(current
->BaseDllName
.Buffer
, Name
, Length
) == 0) ||
124 (Index
>= 0 && Count
++ == Index
))
126 wcsncpy(pInfo
->Name
, current
->BaseDllName
.Buffer
, Length
);
127 pInfo
->Name
[Length
] = L
'\0';
128 pInfo
->Base
= (ULONG_PTR
)current
->DllBase
;
129 pInfo
->Size
= current
->SizeOfImage
;
130 pInfo
->RosSymInfo
= current
->PatchInformation
;
133 current_entry
= current_entry
->Flink
;
136 return KdbpSymFindUserModule(Address
, Name
, Index
-Count
, pInfo
);
139 /*! \brief Find module by address...
141 * \param Address Any address inside the module to look for.
142 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
145 * \retval TRUE Success - module found.
146 * \retval FALSE Failure - module not found.
148 * \sa KdbpSymFindModuleByName
149 * \sa KdbpSymFindModuleByIndex
152 KdbpSymFindModuleByAddress(IN PVOID Address
,
153 OUT PKDB_MODULE_INFO pInfo
)
155 return KdbpSymFindModule(Address
, NULL
, -1, pInfo
);
158 /*! \brief Find module by name...
160 * \param Name Name of the module to look for.
161 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
164 * \retval TRUE Success - module found.
165 * \retval FALSE Failure - module not found.
167 * \sa KdbpSymFindModuleByAddress
168 * \sa KdbpSymFindModuleByIndex
171 KdbpSymFindModuleByName(IN LPCWSTR Name
,
172 OUT PKDB_MODULE_INFO pInfo
)
174 return KdbpSymFindModule(NULL
, Name
, -1, pInfo
);
177 /*! \brief Find module by index...
179 * \param Index Index of the module to return.
180 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
183 * \retval TRUE Success - module found.
184 * \retval FALSE Failure - module not found.
186 * \sa KdbpSymFindModuleByName
187 * \sa KdbpSymFindModuleByAddress
190 KdbpSymFindModuleByIndex(IN INT Index
,
191 OUT PKDB_MODULE_INFO pInfo
)
193 return KdbpSymFindModule(NULL
, NULL
, Index
, pInfo
);
196 /*! \brief Print address...
198 * Tries to lookup line number, file name and function name for the given
199 * address and prints it.
200 * If no such information is found the address is printed in the format
201 * <module: offset>, otherwise the format will be
202 * <module: offset (filename:linenumber (functionname))>
204 * \retval TRUE Module containing \a Address was found, \a Address was printed.
205 * \retval FALSE No module containing \a Address was found, nothing was printed.
208 KdbSymPrintAddress(IN PVOID Address
)
210 KDB_MODULE_INFO Info
;
211 ULONG_PTR RelativeAddress
;
215 CHAR FunctionName
[256];
217 if (!KdbpSymFindModuleByAddress(Address
, &Info
))
220 RelativeAddress
= (ULONG_PTR
) Address
- Info
.Base
;
221 Status
= KdbSymGetAddressInformation(Info
.RosSymInfo
,
226 if (NT_SUCCESS(Status
))
228 DbgPrint("<%ws:%x (%s:%d (%s))>",
229 Info
.Name
, RelativeAddress
, FileName
, LineNumber
, FunctionName
);
233 DbgPrint("<%ws:%x>", Info
.Name
, RelativeAddress
);
240 /*! \brief Get information for an address (source file, line number,
243 * \param SymbolInfo Pointer to ROSSYM_INFO.
244 * \param RelativeAddress Relative address to look up.
245 * \param LineNumber Pointer to an ULONG which is filled with the line
246 * number (can be NULL)
247 * \param FileName Pointer to an array of CHARs which gets filled with
248 * the filename (can be NULL)
249 * \param FunctionName Pointer to an array of CHARs which gets filled with
250 * the function name (can be NULL)
252 * \returns NTSTATUS error code.
253 * \retval STATUS_SUCCESS At least one of the requested informations was found.
254 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
257 KdbSymGetAddressInformation(IN PROSSYM_INFO RosSymInfo
,
258 IN ULONG_PTR RelativeAddress
,
259 OUT PULONG LineNumber OPTIONAL
,
260 OUT PCH FileName OPTIONAL
,
261 OUT PCH FunctionName OPTIONAL
)
263 if (NULL
== RosSymInfo
)
265 return STATUS_UNSUCCESSFUL
;
268 if (! RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineNumber
,
269 FileName
, FunctionName
))
271 return STATUS_UNSUCCESSFUL
;
274 return STATUS_SUCCESS
;
277 /*! \brief Find cached symbol file.
279 * Looks through the list of cached symbol files and tries to find an already
282 * \param FileName FileName of the symbol file to look for.
284 * \returns A pointer to the cached symbol info.
285 * \retval NULL No cached info found.
287 * \sa KdbpSymAddCachedFile
290 KdbpSymFindCachedFile(IN PUNICODE_STRING FileName
)
292 PIMAGE_SYMBOL_INFO_CACHE Current
;
293 PLIST_ENTRY CurrentEntry
;
296 DPRINT("Looking for cached symbol file %wZ\n", FileName
);
298 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
300 CurrentEntry
= SymbolFileListHead
.Flink
;
301 while (CurrentEntry
!= (&SymbolFileListHead
))
303 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
305 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current
->FileName
, FileName
);
306 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
309 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
310 DPRINT("Found cached file!\n");
311 return Current
->RosSymInfo
;
314 CurrentEntry
= CurrentEntry
->Flink
;
317 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
319 DPRINT("Cached file not found!\n");
323 /*! \brief Add a symbol file to the cache.
325 * \param FileName Filename of the symbol file.
326 * \param RosSymInfo Pointer to the symbol info.
328 * \sa KdbpSymRemoveCachedFile
331 KdbpSymAddCachedFile(IN PUNICODE_STRING FileName
,
332 IN PROSSYM_INFO RosSymInfo
)
334 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
336 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo
);
339 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
341 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
344 RtlCreateUnicodeString(&CacheEntry
->FileName
, FileName
->Buffer
);
345 ASSERT(CacheEntry
->FileName
.Buffer
);
346 CacheEntry
->RefCount
= 1;
347 CacheEntry
->RosSymInfo
= RosSymInfo
;
348 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
); /* FIXME: Lock list? */
351 /*! \brief Remove a symbol file (reference) from the cache.
353 * Tries to find a cache entry matching the given symbol info and decreases
354 * it's reference count. If the refcount is 0 after decreasing it the cache
355 * entry will be removed from the list and freed.
357 * \param RosSymInfo Pointer to the symbol info.
359 * \sa KdbpSymAddCachedFile
362 KdbpSymRemoveCachedFile(IN PROSSYM_INFO RosSymInfo
)
364 PIMAGE_SYMBOL_INFO_CACHE Current
;
365 PLIST_ENTRY CurrentEntry
;
368 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
370 CurrentEntry
= SymbolFileListHead
.Flink
;
371 while (CurrentEntry
!= (&SymbolFileListHead
))
373 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
375 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
377 ASSERT(Current
->RefCount
> 0);
379 if (Current
->RefCount
< 1)
381 RemoveEntryList(&Current
->ListEntry
);
382 RosSymDelete(Current
->RosSymInfo
);
385 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
389 CurrentEntry
= CurrentEntry
->Flink
;
392 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
393 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo
);
396 /*! \brief Loads a symbol file.
398 * \param FileName Filename of the symbol file to load.
399 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
401 * \sa KdbpSymUnloadModuleSymbols
404 KdbpSymLoadModuleSymbols(IN PUNICODE_STRING FileName
,
405 OUT PROSSYM_INFO
*RosSymInfo
)
407 OBJECT_ATTRIBUTES ObjectAttributes
;
410 IO_STATUS_BLOCK IoStatusBlock
;
412 /* Allow KDB to break on module load */
413 KdbModuleLoaded(FileName
);
421 /* Try to find cached (already loaded) symbol file */
422 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
423 if (*RosSymInfo
!= NULL
)
425 DPRINT("Found cached symbol file %wZ\n", FileName
);
430 InitializeObjectAttributes(&ObjectAttributes
,
436 DPRINT("Attempting to open image: %wZ\n", FileName
);
438 Status
= ZwOpenFile(&FileHandle
,
442 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
443 FILE_SYNCHRONOUS_IO_NONALERT
);
444 if (!NT_SUCCESS(Status
))
446 DPRINT("Could not open image file: %wZ\n", &FileName
);
450 DPRINT("Loading symbols from %wZ...\n", FileName
);
452 if (! RosSymCreateFromFile(&FileHandle
, RosSymInfo
))
454 DPRINT("Failed to load symbols from %wZ\n", FileName
);
460 DPRINT("Symbols loaded.\n");
462 /* add file to cache */
463 KdbpSymAddCachedFile(FileName
, *RosSymInfo
);
465 DPRINT("Installed symbols: %wZ %p\n", FileName
, *RosSymInfo
);
468 /*! \brief Unloads symbol info.
470 * \param RosSymInfo Pointer to the symbol info to unload.
472 * \sa KdbpSymLoadModuleSymbols
475 KdbpSymUnloadModuleSymbols(IN PROSSYM_INFO RosSymInfo
)
477 DPRINT("Unloading symbols\n");
479 if (RosSymInfo
!= NULL
)
481 KdbpSymRemoveCachedFile(RosSymInfo
);
485 /*! \brief Load symbol info for a user module.
487 * \param LdrModule Pointer to the module to load symbols for.
490 KdbSymLoadUserModuleSymbols(IN PLDR_DATA_TABLE_ENTRY LdrModule
)
492 static WCHAR Prefix
[] = L
"\\??\\";
493 UNICODE_STRING KernelName
;
494 DPRINT("LdrModule %p\n", LdrModule
);
496 LdrModule
->PatchInformation
= NULL
;
498 KernelName
.MaximumLength
= sizeof(Prefix
) + LdrModule
->FullDllName
.Length
;
499 KernelName
.Length
= KernelName
.MaximumLength
- sizeof(WCHAR
);
500 KernelName
.Buffer
= ExAllocatePoolWithTag(PagedPool
, KernelName
.MaximumLength
, TAG_KDBS
);
501 if (NULL
== KernelName
.Buffer
)
505 memcpy(KernelName
.Buffer
, Prefix
, sizeof(Prefix
) - sizeof(WCHAR
));
506 memcpy(KernelName
.Buffer
+ sizeof(Prefix
) / sizeof(WCHAR
) - 1, LdrModule
->FullDllName
.Buffer
,
507 LdrModule
->FullDllName
.Length
);
508 KernelName
.Buffer
[KernelName
.Length
/ sizeof(WCHAR
)] = L
'\0';
510 KdbpSymLoadModuleSymbols(&KernelName
, (PROSSYM_INFO
*)&LdrModule
->PatchInformation
);
512 ExFreePool(KernelName
.Buffer
);
515 /*! \brief Frees all symbols loaded for a process.
517 * \param Process Pointer to a process.
520 KdbSymFreeProcessSymbols(IN PEPROCESS Process
)
522 PLIST_ENTRY CurrentEntry
;
523 PLDR_DATA_TABLE_ENTRY Current
;
524 PEPROCESS CurrentProcess
;
527 CurrentProcess
= PsGetCurrentProcess();
528 if (CurrentProcess
!= Process
)
530 KeAttachProcess(&Process
->Pcb
);
536 CurrentEntry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
537 while (CurrentEntry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
538 CurrentEntry
!= NULL
)
540 Current
= CONTAINING_RECORD(CurrentEntry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderModuleList
);
542 KdbpSymUnloadModuleSymbols(Current
->PatchInformation
);
544 CurrentEntry
= CurrentEntry
->Flink
;
546 if (CurrentProcess
!= Process
)
552 /*! \brief Load symbol info for a driver.
554 * \param Filename Filename of the driver.
555 * \param Module Pointer to the driver LDR_DATA_TABLE_ENTRY.
558 KdbSymLoadDriverSymbols(IN PUNICODE_STRING Filename
,
559 IN PLDR_DATA_TABLE_ENTRY Module
)
561 /* Load symbols for the image if available */
562 DPRINT("Loading driver %wZ symbols (driver @ %08x)\n", Filename
, Module
->DllBase
);
564 Module
->PatchInformation
= NULL
;
566 KdbpSymLoadModuleSymbols(Filename
, (PROSSYM_INFO
*)&Module
->PatchInformation
);
569 /*! \brief Unloads symbol info for a driver.
571 * \param ModuleObject Pointer to the driver LDR_DATA_TABLE_ENTRY.
574 KdbSymUnloadDriverSymbols(IN PLDR_DATA_TABLE_ENTRY ModuleObject
)
576 /* Unload symbols for module if available */
577 KdbpSymUnloadModuleSymbols(ModuleObject
->PatchInformation
);
578 ModuleObject
->PatchInformation
= NULL
;
581 /*! \brief Called when a symbol file is loaded by the loader?
583 * Tries to find a driver (.sys) or executable (.exe) with the same base name
584 * as the symbol file and sets the drivers/exes symbol info to the loaded
586 * Used to load ntoskrnl and hal symbols before the SystemRoot is available to us.
588 * \param FileName Filename for which the symbols are loaded.
591 KdbSymProcessBootSymbols(IN PCHAR FileName
)
593 PLDR_DATA_TABLE_ENTRY ModuleObject
;
594 UNICODE_STRING UnicodeString
;
595 PLOADER_MODULE KeLoaderModules
= (PLOADER_MODULE
)KeLoaderBlock
.ModsAddr
;
596 ANSI_STRING AnsiString
;
600 DPRINT("KdbSymProcessBootSymbols(%s)\n", FileName
);
602 if (0 == _stricmp(FileName
, "ntoskrnl.sym"))
604 RtlInitAnsiString(&AnsiString
, "ntoskrnl.exe");
609 RtlInitAnsiString(&AnsiString
, FileName
);
612 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
613 ModuleObject
= LdrGetModuleObject(&UnicodeString
);
614 RtlFreeUnicodeString(&UnicodeString
);
616 if (ModuleObject
!= NULL
)
620 ModuleObject
->PatchInformation
= NULL
;
624 for (i
= 0; i
< KeLoaderBlock
.ModsCount
; i
++)
626 if (0 == _stricmp(FileName
, (PCHAR
)KeLoaderModules
[i
].String
))
631 if (i
< KeLoaderBlock
.ModsCount
)
633 KeLoaderModules
[i
].Reserved
= 1;
634 if (ModuleObject
->PatchInformation
!= NULL
)
636 KdbpSymRemoveCachedFile(ModuleObject
->PatchInformation
);
641 if (! RosSymCreateFromRaw((PVOID
) KeLoaderModules
[i
].ModStart
,
642 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
643 (PROSSYM_INFO
*)&ModuleObject
->PatchInformation
))
650 if (! RosSymCreateFromMem((PVOID
) KeLoaderModules
[i
].ModStart
,
651 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
652 (PROSSYM_INFO
*)&ModuleObject
->PatchInformation
))
658 /* add file to cache */
659 RtlInitAnsiString(&AnsiString
, FileName
);
660 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
661 KdbpSymAddCachedFile(&UnicodeString
, ModuleObject
->PatchInformation
);
662 RtlFreeUnicodeString(&UnicodeString
);
664 DPRINT("Installed symbols: %s@%08x-%08x %p\n",
666 ModuleObject
->DllBase
,
667 ModuleObject
->SizeOfImage
+ (ULONG
)ModuleObject
->DllBase
,
668 ModuleObject
->PatchInformation
);
673 /*! \brief Initializes the KDB symbols implementation.
675 * \param NtoskrnlModuleObject LDR_DATA_TABLE_ENTRY of ntoskrnl.exe
676 * \param LdrHalModuleObject LDR_DATA_TABLE_ENTRY of hal.sys
679 KdbSymInit(IN PLDR_DATA_TABLE_ENTRY NtoskrnlModuleObject
,
680 IN PLDR_DATA_TABLE_ENTRY LdrHalModuleObject
)
686 NtoskrnlModuleObject
->PatchInformation
= NULL
;
687 LdrHalModuleObject
->PatchInformation
= NULL
;
689 InitializeListHead(&SymbolFileListHead
);
690 KeInitializeSpinLock(&SymbolFileListLock
);
698 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
699 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
700 p1
= (PCHAR
) KeLoaderBlock
.CommandLine
;
701 while('\0' != *p1
&& NULL
!= (p2
= strchr(p1
, '/')))
705 if (0 == _strnicmp(p2
, "LOADSYMBOLS", 11))
710 else if (0 == _strnicmp(p2
, "NOLOADSYMBOLS", 13))
728 YesNo
= toupper(*p2
);
729 if ('N' == YesNo
|| 'F' == YesNo
|| '0' == YesNo
)
734 LoadSymbols
= (0 < Found
);
739 RosSymInitKernelMode();