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
;
55 PEPROCESS CurrentProcess
;
59 CurrentProcess
= PsGetCurrentProcess();
60 if (CurrentProcess
!= NULL
)
62 Peb
= CurrentProcess
->Peb
;
65 if (Peb
== NULL
|| Peb
->Ldr
== NULL
)
70 current_entry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
72 while (current_entry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
73 current_entry
!= NULL
)
75 current
= CONTAINING_RECORD(current_entry
, LDR_MODULE
, InLoadOrderModuleList
);
77 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->BaseAddress
&&
78 Address
< (PVOID
)((char *)current
->BaseAddress
+ current
->ResidentSize
))) ||
79 (Name
!= NULL
&& _wcsicmp(current
->BaseDllName
.Buffer
, Name
) == 0) ||
80 (Index
>= 0 && Count
++ == Index
))
82 INT Length
= current
->BaseDllName
.Length
;
85 wcsncpy(pInfo
->Name
, current
->BaseDllName
.Buffer
, Length
);
86 pInfo
->Name
[Length
] = L
'\0';
87 pInfo
->Base
= (ULONG_PTR
)current
->BaseAddress
;
88 pInfo
->Size
= current
->ResidentSize
;
89 pInfo
->RosSymInfo
= current
->RosSymInfo
;
92 current_entry
= current_entry
->Flink
;
98 /*! \brief Find a kernel-mode module...
100 * Works like \a KdbpSymFindUserModule.
102 * \sa KdbpSymFindUserModule
105 KdbpSymFindModule(IN PVOID Address OPTIONAL
,
106 IN LPCWSTR Name OPTIONAL
,
107 IN INT Index OPTIONAL
,
108 OUT PKDB_MODULE_INFO pInfo
)
110 PLIST_ENTRY current_entry
;
111 MODULE_TEXT_SECTION
* current
;
112 extern LIST_ENTRY ModuleTextListHead
;
115 current_entry
= ModuleTextListHead
.Flink
;
117 while (current_entry
!= &ModuleTextListHead
&&
118 current_entry
!= NULL
)
120 current
= CONTAINING_RECORD(current_entry
, MODULE_TEXT_SECTION
, ListEntry
);
122 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->Base
&&
123 Address
< (PVOID
)(current
->Base
+ current
->Length
))) ||
124 (Name
!= NULL
&& _wcsicmp(current
->Name
, Name
) == 0) ||
125 (Index
>= 0 && Count
++ == Index
))
127 wcsncpy(pInfo
->Name
, current
->Name
, 255);
128 pInfo
->Name
[255] = L
'\0';
129 pInfo
->Base
= (ULONG_PTR
)current
->Base
;
130 pInfo
->Size
= current
->Length
;
131 pInfo
->RosSymInfo
= current
->RosSymInfo
;
134 current_entry
= current_entry
->Flink
;
137 return KdbpSymFindUserModule(Address
, Name
, Index
-Count
, pInfo
);
140 /*! \brief Find module by address...
142 * \param Address Any address inside the module to look for.
143 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
146 * \retval TRUE Success - module found.
147 * \retval FALSE Failure - module not found.
149 * \sa KdbpSymFindModuleByName
150 * \sa KdbpSymFindModuleByIndex
153 KdbpSymFindModuleByAddress(IN PVOID Address
,
154 OUT PKDB_MODULE_INFO pInfo
)
156 return KdbpSymFindModule(Address
, NULL
, -1, pInfo
);
159 /*! \brief Find module by name...
161 * \param Name Name of the module to look for.
162 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
165 * \retval TRUE Success - module found.
166 * \retval FALSE Failure - module not found.
168 * \sa KdbpSymFindModuleByAddress
169 * \sa KdbpSymFindModuleByIndex
172 KdbpSymFindModuleByName(IN LPCWSTR Name
,
173 OUT PKDB_MODULE_INFO pInfo
)
175 return KdbpSymFindModule(NULL
, Name
, -1, pInfo
);
178 /*! \brief Find module by index...
180 * \param Index Index of the module to return.
181 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
184 * \retval TRUE Success - module found.
185 * \retval FALSE Failure - module not found.
187 * \sa KdbpSymFindModuleByName
188 * \sa KdbpSymFindModuleByAddress
191 KdbpSymFindModuleByIndex(IN INT Index
,
192 OUT PKDB_MODULE_INFO pInfo
)
194 return KdbpSymFindModule(NULL
, NULL
, Index
, pInfo
);
197 /*! \brief Print address...
199 * Tries to lookup line number, file name and function name for the given
200 * address and prints it.
201 * If no such information is found the address is printed in the format
202 * <module: offset>, otherwise the format will be
203 * <module: offset (filename:linenumber (functionname))>
205 * \retval TRUE Module containing \a Address was found, \a Address was printed.
206 * \retval FALSE No module containing \a Address was found, nothing was printed.
209 KdbSymPrintAddress(IN PVOID Address
)
211 KDB_MODULE_INFO Info
;
212 ULONG_PTR RelativeAddress
;
216 CHAR FunctionName
[256];
218 if (!KdbpSymFindModuleByAddress(Address
, &Info
))
221 RelativeAddress
= (ULONG_PTR
) Address
- Info
.Base
;
222 Status
= KdbSymGetAddressInformation(Info
.RosSymInfo
,
227 if (NT_SUCCESS(Status
))
229 DbgPrint("<%ws:%x (%s:%d (%s))>",
230 Info
.Name
, RelativeAddress
, FileName
, LineNumber
, FunctionName
);
234 DbgPrint("<%ws:%x>", Info
.Name
, RelativeAddress
);
241 /*! \brief Get information for an address (source file, line number,
244 * \param SymbolInfo Pointer to ROSSYM_INFO.
245 * \param RelativeAddress Relative address to look up.
246 * \param LineNumber Pointer to an ULONG which is filled with the line
247 * number (can be NULL)
248 * \param FileName Pointer to an array of CHARs which gets filled with
249 * the filename (can be NULL)
250 * \param FunctionName Pointer to an array of CHARs which gets filled with
251 * the function name (can be NULL)
253 * \returns NTSTATUS error code.
254 * \retval STATUS_SUCCESS At least one of the requested informations was found.
255 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
258 KdbSymGetAddressInformation(IN PROSSYM_INFO RosSymInfo
,
259 IN ULONG_PTR RelativeAddress
,
260 OUT PULONG LineNumber OPTIONAL
,
261 OUT PCH FileName OPTIONAL
,
262 OUT PCH FunctionName OPTIONAL
)
264 if (NULL
== RosSymInfo
)
266 return STATUS_UNSUCCESSFUL
;
269 if (! RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineNumber
,
270 FileName
, FunctionName
))
272 return STATUS_UNSUCCESSFUL
;
275 return STATUS_SUCCESS
;
278 /*! \brief Find cached symbol file.
280 * Looks through the list of cached symbol files and tries to find an already
283 * \param FileName FileName of the symbol file to look for.
285 * \returns A pointer to the cached symbol info.
286 * \retval NULL No cached info found.
288 * \sa KdbpSymAddCachedFile
291 KdbpSymFindCachedFile(IN PUNICODE_STRING FileName
)
293 PIMAGE_SYMBOL_INFO_CACHE Current
;
294 PLIST_ENTRY CurrentEntry
;
297 DPRINT("Looking for cached symbol file %wZ\n", FileName
);
299 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
301 CurrentEntry
= SymbolFileListHead
.Flink
;
302 while (CurrentEntry
!= (&SymbolFileListHead
))
304 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
306 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current
->FileName
, FileName
);
307 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
310 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
311 DPRINT("Found cached file!\n");
312 return Current
->RosSymInfo
;
315 CurrentEntry
= CurrentEntry
->Flink
;
318 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
320 DPRINT("Cached file not found!\n");
324 /*! \brief Add a symbol file to the cache.
326 * \param FileName Filename of the symbol file.
327 * \param RosSymInfo Pointer to the symbol info.
329 * \sa KdbpSymRemoveCachedFile
332 KdbpSymAddCachedFile(IN PUNICODE_STRING FileName
,
333 IN PROSSYM_INFO RosSymInfo
)
335 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
337 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo
);
340 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
342 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
345 RtlpCreateUnicodeString(&CacheEntry
->FileName
, FileName
->Buffer
, PagedPool
);
346 ASSERT(CacheEntry
->FileName
.Buffer
);
347 CacheEntry
->RefCount
= 1;
348 CacheEntry
->RosSymInfo
= RosSymInfo
;
349 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
); /* FIXME: Lock list? */
352 /*! \brief Remove a symbol file (reference) from the cache.
354 * Tries to find a cache entry matching the given symbol info and decreases
355 * it's reference count. If the refcount is 0 after decreasing it the cache
356 * entry will be removed from the list and freed.
358 * \param RosSymInfo Pointer to the symbol info.
360 * \sa KdbpSymAddCachedFile
363 KdbpSymRemoveCachedFile(IN PROSSYM_INFO RosSymInfo
)
365 PIMAGE_SYMBOL_INFO_CACHE Current
;
366 PLIST_ENTRY CurrentEntry
;
369 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
371 CurrentEntry
= SymbolFileListHead
.Flink
;
372 while (CurrentEntry
!= (&SymbolFileListHead
))
374 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
376 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
378 ASSERT(Current
->RefCount
> 0);
380 if (Current
->RefCount
< 1)
382 RemoveEntryList(&Current
->ListEntry
);
383 RosSymDelete(Current
->RosSymInfo
);
386 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
390 CurrentEntry
= CurrentEntry
->Flink
;
393 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
394 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo
);
397 /*! \brief Loads a symbol file.
399 * \param FileName Filename of the symbol file to load.
400 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
402 * \sa KdbpSymUnloadModuleSymbols
405 KdbpSymLoadModuleSymbols(IN PUNICODE_STRING FileName
,
406 OUT PROSSYM_INFO
*RosSymInfo
)
408 OBJECT_ATTRIBUTES ObjectAttributes
;
411 IO_STATUS_BLOCK IoStatusBlock
;
413 /* Allow KDB to break on module load */
414 KdbModuleLoaded(FileName
);
422 /* Try to find cached (already loaded) symbol file */
423 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
424 if (*RosSymInfo
!= NULL
)
426 DPRINT("Found cached symbol file %wZ\n", FileName
);
431 InitializeObjectAttributes(&ObjectAttributes
,
437 DPRINT("Attempting to open image: %wZ\n", FileName
);
439 Status
= ZwOpenFile(&FileHandle
,
443 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
444 FILE_SYNCHRONOUS_IO_NONALERT
);
445 if (!NT_SUCCESS(Status
))
447 DPRINT("Could not open image file: %wZ\n", &FileName
);
451 DPRINT("Loading symbols from %wZ...\n", FileName
);
453 if (! RosSymCreateFromFile(&FileHandle
, RosSymInfo
))
455 DPRINT("Failed to load symbols from %wZ\n", FileName
);
461 DPRINT("Symbols loaded.\n");
463 /* add file to cache */
464 KdbpSymAddCachedFile(FileName
, *RosSymInfo
);
466 DPRINT("Installed symbols: %wZ %p\n", FileName
, *RosSymInfo
);
469 /*! \brief Unloads symbol info.
471 * \param RosSymInfo Pointer to the symbol info to unload.
473 * \sa KdbpSymLoadModuleSymbols
476 KdbpSymUnloadModuleSymbols(IN PROSSYM_INFO RosSymInfo
)
478 DPRINT("Unloading symbols\n");
480 if (RosSymInfo
!= NULL
)
482 KdbpSymRemoveCachedFile(RosSymInfo
);
486 /*! \brief Load symbol info for a user module.
488 * \param LdrModule Pointer to the module to load symbols for.
491 KdbSymLoadUserModuleSymbols(IN PLDR_MODULE LdrModule
)
493 static WCHAR Prefix
[] = L
"\\??\\";
494 UNICODE_STRING KernelName
;
495 DPRINT("LdrModule %p\n", LdrModule
);
497 LdrModule
->RosSymInfo
= NULL
;
499 KernelName
.MaximumLength
= sizeof(Prefix
) + LdrModule
->FullDllName
.Length
;
500 KernelName
.Length
= KernelName
.MaximumLength
- sizeof(WCHAR
);
501 KernelName
.Buffer
= ExAllocatePoolWithTag(PagedPool
, KernelName
.MaximumLength
, TAG_KDBS
);
502 if (NULL
== KernelName
.Buffer
)
506 memcpy(KernelName
.Buffer
, Prefix
, sizeof(Prefix
) - sizeof(WCHAR
));
507 memcpy(KernelName
.Buffer
+ sizeof(Prefix
) / sizeof(WCHAR
) - 1, LdrModule
->FullDllName
.Buffer
,
508 LdrModule
->FullDllName
.Length
);
509 KernelName
.Buffer
[KernelName
.Length
/ sizeof(WCHAR
)] = L
'\0';
511 KdbpSymLoadModuleSymbols(&KernelName
, &LdrModule
->RosSymInfo
);
513 ExFreePool(KernelName
.Buffer
);
516 /*! \brief Frees all symbols loaded for a process.
518 * \param Process Pointer to a process.
521 KdbSymFreeProcessSymbols(IN PEPROCESS Process
)
523 PLIST_ENTRY CurrentEntry
;
525 PEPROCESS CurrentProcess
;
528 CurrentProcess
= PsGetCurrentProcess();
529 if (CurrentProcess
!= Process
)
531 KeAttachProcess(EPROCESS_TO_KPROCESS(Process
));
537 CurrentEntry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
538 while (CurrentEntry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
539 CurrentEntry
!= NULL
)
541 Current
= CONTAINING_RECORD(CurrentEntry
, LDR_MODULE
, InLoadOrderModuleList
);
543 KdbpSymUnloadModuleSymbols(Current
->RosSymInfo
);
545 CurrentEntry
= CurrentEntry
->Flink
;
547 if (CurrentProcess
!= Process
)
553 /*! \brief Load symbol info for a driver.
555 * \param Filename Filename of the driver.
556 * \param Module Pointer to the driver MODULE_OBJECT.
559 KdbSymLoadDriverSymbols(IN PUNICODE_STRING Filename
,
560 IN PMODULE_OBJECT Module
)
562 /* Load symbols for the image if available */
563 DPRINT("Loading driver %wZ symbols (driver @ %08x)\n", Filename
, Module
->Base
);
565 Module
->TextSection
->RosSymInfo
= NULL
;
567 KdbpSymLoadModuleSymbols(Filename
, &Module
->TextSection
->RosSymInfo
);
570 /*! \brief Unloads symbol info for a driver.
572 * \param ModuleObject Pointer to the driver MODULE_OBJECT.
575 KdbSymUnloadDriverSymbols(IN PMODULE_OBJECT ModuleObject
)
577 /* Unload symbols for module if available */
578 KdbpSymUnloadModuleSymbols(ModuleObject
->TextSection
->RosSymInfo
);
579 ModuleObject
->TextSection
->RosSymInfo
= NULL
;
582 /*! \brief Called when a symbol file is loaded by the loader?
584 * Tries to find a driver (.sys) or executable (.exe) with the same base name
585 * as the symbol file and sets the drivers/exes symbol info to the loaded
587 * Used to load ntoskrnl and hal symbols before the SystemRoot is available to us.
589 * \param FileName Filename for which the symbols are loaded.
592 KdbSymProcessBootSymbols(IN PCHAR FileName
)
594 PMODULE_OBJECT ModuleObject
;
595 UNICODE_STRING UnicodeString
;
596 PLOADER_MODULE KeLoaderModules
= (PLOADER_MODULE
)KeLoaderBlock
.ModsAddr
;
597 ANSI_STRING AnsiString
;
601 DPRINT("KdbSymProcessBootSymbols(%s)\n", FileName
);
603 if (0 == _stricmp(FileName
, "ntoskrnl.sym"))
605 RtlInitAnsiString(&AnsiString
, "ntoskrnl.exe");
610 RtlInitAnsiString(&AnsiString
, FileName
);
613 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
614 ModuleObject
= LdrGetModuleObject(&UnicodeString
);
615 RtlFreeUnicodeString(&UnicodeString
);
617 if (ModuleObject
!= NULL
)
621 ModuleObject
->TextSection
->RosSymInfo
= NULL
;
625 for (i
= 0; i
< KeLoaderBlock
.ModsCount
; i
++)
627 if (0 == _stricmp(FileName
, (PCHAR
)KeLoaderModules
[i
].String
))
632 if (i
< KeLoaderBlock
.ModsCount
)
634 KeLoaderModules
[i
].Reserved
= 1;
635 if (ModuleObject
->TextSection
->RosSymInfo
!= NULL
)
637 KdbpSymRemoveCachedFile(ModuleObject
->TextSection
->RosSymInfo
);
642 if (! RosSymCreateFromRaw((PVOID
) KeLoaderModules
[i
].ModStart
,
643 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
644 &ModuleObject
->TextSection
->RosSymInfo
))
651 if (! RosSymCreateFromMem((PVOID
) KeLoaderModules
[i
].ModStart
,
652 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
653 &ModuleObject
->TextSection
->RosSymInfo
))
659 /* add file to cache */
660 RtlInitAnsiString(&AnsiString
, FileName
);
661 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
662 KdbpSymAddCachedFile(&UnicodeString
, ModuleObject
->TextSection
->RosSymInfo
);
663 RtlFreeUnicodeString(&UnicodeString
);
665 DPRINT("Installed symbols: %s@%08x-%08x %p\n",
668 ModuleObject
->Length
+ ModuleObject
->Base
,
669 ModuleObject
->TextSection
->RosSymInfo
);
674 /*! \brief Initializes the KDB symbols implementation.
676 * \param NtoskrnlTextSection MODULE_TEXT_SECTION of ntoskrnl.exe
677 * \param LdrHalTextSection MODULE_TEXT_SECTION of hal.sys
680 KdbSymInit(IN PMODULE_TEXT_SECTION NtoskrnlTextSection
,
681 IN PMODULE_TEXT_SECTION LdrHalTextSection
)
687 NtoskrnlTextSection
->RosSymInfo
= NULL
;
688 LdrHalTextSection
->RosSymInfo
= NULL
;
690 InitializeListHead(&SymbolFileListHead
);
691 KeInitializeSpinLock(&SymbolFileListLock
);
699 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
700 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
701 p1
= (PCHAR
) KeLoaderBlock
.CommandLine
;
702 while('\0' != *p1
&& NULL
!= (p2
= strchr(p1
, '/')))
706 if (0 == _strnicmp(p2
, "LOADSYMBOLS", 11))
711 else if (0 == _strnicmp(p2
, "NOLOADSYMBOLS", 13))
729 YesNo
= toupper(*p2
);
730 if ('N' == YesNo
|| 'F' == YesNo
|| '0' == YesNo
)
735 LoadSymbols
= (0 < Found
);
740 RosSymInitKernelMode();