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 *****************************************************************/
12 #include <ddk/ntddk.h>
15 #include <reactos/rossym.h>
18 #include <internal/debug.h>
20 /* GLOBALS ******************************************************************/
22 typedef struct _IMAGE_SYMBOL_INFO_CACHE
{
25 UNICODE_STRING FileName
;
26 PROSSYM_INFO RosSymInfo
;
27 } IMAGE_SYMBOL_INFO_CACHE
, *PIMAGE_SYMBOL_INFO_CACHE
;
29 static BOOLEAN LoadSymbols
;
30 static LIST_ENTRY SymbolFileListHead
;
31 static KSPIN_LOCK SymbolFileListLock
;
34 /* FUNCTIONS ****************************************************************/
36 /*! \brief Find a user-mode module...
38 * \param Address If \a Address is not NULL the module containing \a Address
40 * \param Name If \a Name is not NULL the module named \a Name will be
42 * \param Index If \a Index is >= 0 the Index'th module will be returned.
43 * \param pInfo Pointer to a KDB_MODULE_INFO which is filled.
45 * \retval TRUE Module was found, \a pInfo was filled.
46 * \retval FALSE No module was found.
48 * \sa KdbpSymFindModule
51 KdbpSymFindUserModule(IN PVOID Address OPTIONAL
,
52 IN LPCWSTR Name OPTIONAL
,
53 IN INT Index OPTIONAL
,
54 OUT PKDB_MODULE_INFO pInfo
)
56 PLIST_ENTRY current_entry
;
58 PEPROCESS CurrentProcess
;
62 CurrentProcess
= PsGetCurrentProcess();
63 if (CurrentProcess
!= NULL
)
65 Peb
= CurrentProcess
->Peb
;
68 if (Peb
== NULL
|| Peb
->Ldr
== NULL
)
73 current_entry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
75 while (current_entry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
76 current_entry
!= NULL
)
78 current
= CONTAINING_RECORD(current_entry
, LDR_MODULE
, InLoadOrderModuleList
);
80 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->BaseAddress
&&
81 Address
< (PVOID
)((char *)current
->BaseAddress
+ current
->ResidentSize
))) ||
82 (Name
!= NULL
&& _wcsicmp(current
->BaseDllName
.Buffer
, Name
) == 0) ||
83 (Index
>= 0 && Count
++ == Index
))
85 INT Length
= current
->BaseDllName
.Length
;
88 wcsncpy(pInfo
->Name
, current
->BaseDllName
.Buffer
, Length
);
89 pInfo
->Name
[Length
] = L
'\0';
90 pInfo
->Base
= (ULONG_PTR
)current
->BaseAddress
;
91 pInfo
->Size
= current
->ResidentSize
;
92 pInfo
->RosSymInfo
= current
->RosSymInfo
;
95 current_entry
= current_entry
->Flink
;
101 /*! \brief Find a kernel-mode module...
103 * Works like \a KdbpSymFindUserModule.
105 * \sa KdbpSymFindUserModule
108 KdbpSymFindModule(IN PVOID Address OPTIONAL
,
109 IN LPCWSTR Name OPTIONAL
,
110 IN INT Index OPTIONAL
,
111 OUT PKDB_MODULE_INFO pInfo
)
113 PLIST_ENTRY current_entry
;
114 MODULE_TEXT_SECTION
* current
;
115 extern LIST_ENTRY ModuleTextListHead
;
118 current_entry
= ModuleTextListHead
.Flink
;
120 while (current_entry
!= &ModuleTextListHead
&&
121 current_entry
!= NULL
)
123 current
= CONTAINING_RECORD(current_entry
, MODULE_TEXT_SECTION
, ListEntry
);
125 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->Base
&&
126 Address
< (PVOID
)(current
->Base
+ current
->Length
))) ||
127 (Name
!= NULL
&& _wcsicmp(current
->Name
, Name
) == 0) ||
128 (Index
>= 0 && Count
++ == Index
))
130 wcsncpy(pInfo
->Name
, current
->Name
, 255);
131 pInfo
->Name
[255] = L
'\0';
132 pInfo
->Base
= (ULONG_PTR
)current
->Base
;
133 pInfo
->Size
= current
->Length
;
134 pInfo
->RosSymInfo
= current
->RosSymInfo
;
137 current_entry
= current_entry
->Flink
;
140 return KdbpSymFindUserModule(Address
, Name
, Index
-Count
, pInfo
);
143 /*! \brief Find module by address...
145 * \param Address Any address inside the module to look for.
146 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
149 * \retval TRUE Success - module found.
150 * \retval FALSE Failure - module not found.
152 * \sa KdbpSymFindModuleByName
153 * \sa KdbpSymFindModuleByIndex
156 KdbpSymFindModuleByAddress(IN PVOID Address
,
157 OUT PKDB_MODULE_INFO pInfo
)
159 return KdbpSymFindModule(Address
, NULL
, -1, pInfo
);
162 /*! \brief Find module by name...
164 * \param Name Name of the module to look for.
165 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
168 * \retval TRUE Success - module found.
169 * \retval FALSE Failure - module not found.
171 * \sa KdbpSymFindModuleByAddress
172 * \sa KdbpSymFindModuleByIndex
175 KdbpSymFindModuleByName(IN LPCWSTR Name
,
176 OUT PKDB_MODULE_INFO pInfo
)
178 return KdbpSymFindModule(NULL
, Name
, -1, pInfo
);
181 /*! \brief Find module by index...
183 * \param Index Index of the module to return.
184 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
187 * \retval TRUE Success - module found.
188 * \retval FALSE Failure - module not found.
190 * \sa KdbpSymFindModuleByName
191 * \sa KdbpSymFindModuleByAddress
194 KdbpSymFindModuleByIndex(IN INT Index
,
195 OUT PKDB_MODULE_INFO pInfo
)
197 return KdbpSymFindModule(NULL
, NULL
, Index
, pInfo
);
200 /*! \brief Print address...
202 * Tries to lookup line number, file name and function name for the given
203 * address and prints it.
204 * If no such information is found the address is printed in the format
205 * <module: offset>, otherwise the format will be
206 * <module: offset (filename:linenumber (functionname))>
208 * \retval TRUE Module containing \a Address was found, \a Address was printed.
209 * \retval FALSE No module containing \a Address was found, nothing was printed.
212 KdbSymPrintAddress(IN PVOID Address
)
214 KDB_MODULE_INFO Info
;
215 ULONG_PTR RelativeAddress
;
219 CHAR FunctionName
[256];
221 if (!KdbpSymFindModuleByAddress(Address
, &Info
))
224 RelativeAddress
= (ULONG_PTR
) Address
- Info
.Base
;
225 Status
= KdbSymGetAddressInformation(Info
.RosSymInfo
,
230 if (NT_SUCCESS(Status
))
232 DbgPrint("<%ws:%x (%s:%d (%s))>",
233 Info
.Name
, RelativeAddress
, FileName
, LineNumber
, FunctionName
);
237 DbgPrint("<%ws:%x>", Info
.Name
, RelativeAddress
);
244 /*! \brief Get information for an address (source file, line number,
247 * \param SymbolInfo Pointer to ROSSYM_INFO.
248 * \param RelativeAddress Relative address to look up.
249 * \param LineNumber Pointer to an ULONG which is filled with the line
250 * number (can be NULL)
251 * \param FileName Pointer to an array of CHARs which gets filled with
252 * the filename (can be NULL)
253 * \param FunctionName Pointer to an array of CHARs which gets filled with
254 * the function name (can be NULL)
256 * \returns NTSTATUS error code.
257 * \retval STATUS_SUCCESS At least one of the requested informations was found.
258 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
261 KdbSymGetAddressInformation(IN PROSSYM_INFO RosSymInfo
,
262 IN ULONG_PTR RelativeAddress
,
263 OUT PULONG LineNumber OPTIONAL
,
264 OUT PCH FileName OPTIONAL
,
265 OUT PCH FunctionName OPTIONAL
)
267 if (NULL
== RosSymInfo
)
269 return STATUS_UNSUCCESSFUL
;
272 if (! RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineNumber
,
273 FileName
, FunctionName
))
275 return STATUS_UNSUCCESSFUL
;
278 return STATUS_SUCCESS
;
281 /*! \brief Find cached symbol file.
283 * Looks through the list of cached symbol files and tries to find an already
286 * \param FileName FileName of the symbol file to look for.
288 * \returns A pointer to the cached symbol info.
289 * \retval NULL No cached info found.
291 * \sa KdbpSymAddCachedFile
294 KdbpSymFindCachedFile(IN PUNICODE_STRING FileName
)
296 PIMAGE_SYMBOL_INFO_CACHE Current
;
297 PLIST_ENTRY CurrentEntry
;
300 DPRINT("Looking for cached symbol file %wZ\n", FileName
);
302 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
304 CurrentEntry
= SymbolFileListHead
.Flink
;
305 while (CurrentEntry
!= (&SymbolFileListHead
))
307 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
309 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current
->FileName
, FileName
);
310 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
313 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
314 DPRINT("Found cached file!\n");
315 return Current
->RosSymInfo
;
318 CurrentEntry
= CurrentEntry
->Flink
;
321 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
323 DPRINT("Cached file not found!\n");
327 /*! \brief Add a symbol file to the cache.
329 * \param FileName Filename of the symbol file.
330 * \param RosSymInfo Pointer to the symbol info.
332 * \sa KdbpSymRemoveCachedFile
335 KdbpSymAddCachedFile(IN PUNICODE_STRING FileName
,
336 IN PROSSYM_INFO RosSymInfo
)
338 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
340 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo
);
343 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
345 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
348 RtlpCreateUnicodeString(&CacheEntry
->FileName
, FileName
->Buffer
, PagedPool
);
349 ASSERT(CacheEntry
->FileName
.Buffer
);
350 CacheEntry
->RefCount
= 1;
351 CacheEntry
->RosSymInfo
= RosSymInfo
;
352 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
); /* FIXME: Lock list? */
355 /*! \brief Remove a symbol file (reference) from the cache.
357 * Tries to find a cache entry matching the given symbol info and decreases
358 * it's reference count. If the refcount is 0 after decreasing it the cache
359 * entry will be removed from the list and freed.
361 * \param RosSymInfo Pointer to the symbol info.
363 * \sa KdbpSymAddCachedFile
366 KdbpSymRemoveCachedFile(IN PROSSYM_INFO RosSymInfo
)
368 PIMAGE_SYMBOL_INFO_CACHE Current
;
369 PLIST_ENTRY CurrentEntry
;
372 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
374 CurrentEntry
= SymbolFileListHead
.Flink
;
375 while (CurrentEntry
!= (&SymbolFileListHead
))
377 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
379 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
381 ASSERT(Current
->RefCount
> 0);
383 if (Current
->RefCount
< 1)
385 RemoveEntryList(&Current
->ListEntry
);
386 RosSymDelete(Current
->RosSymInfo
);
389 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
393 CurrentEntry
= CurrentEntry
->Flink
;
396 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
397 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo
);
400 /*! \brief Loads a symbol file.
402 * \param FileName Filename of the symbol file to load.
403 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
405 * \sa KdbpSymUnloadModuleSymbols
408 KdbpSymLoadModuleSymbols(IN PUNICODE_STRING FileName
,
409 OUT PROSSYM_INFO
*RosSymInfo
)
411 OBJECT_ATTRIBUTES ObjectAttributes
;
414 IO_STATUS_BLOCK IoStatusBlock
;
416 /* Allow KDB to break on module load */
417 KdbModuleLoaded(FileName
);
425 /* Try to find cached (already loaded) symbol file */
426 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
427 if (*RosSymInfo
!= NULL
)
429 DPRINT("Found cached symbol file %wZ\n", FileName
);
434 InitializeObjectAttributes(&ObjectAttributes
,
440 DPRINT("Attempting to open image: %wZ\n", FileName
);
442 Status
= ZwOpenFile(&FileHandle
,
446 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
447 FILE_SYNCHRONOUS_IO_NONALERT
);
448 if (!NT_SUCCESS(Status
))
450 DPRINT("Could not open image file: %wZ\n", &FileName
);
454 DPRINT("Loading symbols from %wZ...\n", FileName
);
456 if (! RosSymCreateFromFile(&FileHandle
, RosSymInfo
))
458 DPRINT("Failed to load symbols from %wZ\n", FileName
);
464 DPRINT("Symbols loaded.\n");
466 /* add file to cache */
467 KdbpSymAddCachedFile(FileName
, *RosSymInfo
);
469 DPRINT("Installed symbols: %wZ %p\n", FileName
, *RosSymInfo
);
472 /*! \brief Unloads symbol info.
474 * \param RosSymInfo Pointer to the symbol info to unload.
476 * \sa KdbpSymLoadModuleSymbols
479 KdbpSymUnloadModuleSymbols(IN PROSSYM_INFO RosSymInfo
)
481 DPRINT("Unloading symbols\n");
483 if (RosSymInfo
!= NULL
)
485 KdbpSymRemoveCachedFile(RosSymInfo
);
489 /*! \brief Load symbol info for a user module.
491 * \param LdrModule Pointer to the module to load symbols for.
494 KdbSymLoadUserModuleSymbols(IN PLDR_MODULE LdrModule
)
496 static WCHAR Prefix
[] = L
"\\??\\";
497 UNICODE_STRING KernelName
;
498 DPRINT("LdrModule %p\n", LdrModule
);
500 LdrModule
->RosSymInfo
= NULL
;
502 KernelName
.MaximumLength
= sizeof(Prefix
) + LdrModule
->FullDllName
.Length
;
503 KernelName
.Length
= KernelName
.MaximumLength
- sizeof(WCHAR
);
504 KernelName
.Buffer
= ExAllocatePoolWithTag(PagedPool
, KernelName
.MaximumLength
, TAG_KDBS
);
505 if (NULL
== KernelName
.Buffer
)
509 memcpy(KernelName
.Buffer
, Prefix
, sizeof(Prefix
) - sizeof(WCHAR
));
510 memcpy(KernelName
.Buffer
+ sizeof(Prefix
) / sizeof(WCHAR
) - 1, LdrModule
->FullDllName
.Buffer
,
511 LdrModule
->FullDllName
.Length
);
512 KernelName
.Buffer
[KernelName
.Length
/ sizeof(WCHAR
)] = L
'\0';
514 KdbpSymLoadModuleSymbols(&KernelName
, &LdrModule
->RosSymInfo
);
516 ExFreePool(KernelName
.Buffer
);
519 /*! \brief Frees all symbols loaded for a process.
521 * \param Process Pointer to a process.
524 KdbSymFreeProcessSymbols(IN PEPROCESS Process
)
526 PLIST_ENTRY CurrentEntry
;
528 PEPROCESS CurrentProcess
;
531 CurrentProcess
= PsGetCurrentProcess();
532 if (CurrentProcess
!= Process
)
534 KeAttachProcess(EPROCESS_TO_KPROCESS(Process
));
540 CurrentEntry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
541 while (CurrentEntry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
542 CurrentEntry
!= NULL
)
544 Current
= CONTAINING_RECORD(CurrentEntry
, LDR_MODULE
, InLoadOrderModuleList
);
546 KdbpSymUnloadModuleSymbols(Current
->RosSymInfo
);
548 CurrentEntry
= CurrentEntry
->Flink
;
550 if (CurrentProcess
!= Process
)
556 /*! \brief Load symbol info for a driver.
558 * \param Filename Filename of the driver.
559 * \param Module Pointer to the driver MODULE_OBJECT.
562 KdbSymLoadDriverSymbols(IN PUNICODE_STRING Filename
,
563 IN PMODULE_OBJECT Module
)
565 /* Load symbols for the image if available */
566 DPRINT("Loading driver %wZ symbols (driver @ %08x)\n", Filename
, Module
->Base
);
568 Module
->TextSection
->RosSymInfo
= NULL
;
570 KdbpSymLoadModuleSymbols(Filename
, &Module
->TextSection
->RosSymInfo
);
573 /*! \brief Unloads symbol info for a driver.
575 * \param ModuleObject Pointer to the driver MODULE_OBJECT.
578 KdbSymUnloadDriverSymbols(IN PMODULE_OBJECT ModuleObject
)
580 /* Unload symbols for module if available */
581 KdbpSymUnloadModuleSymbols(ModuleObject
->TextSection
->RosSymInfo
);
582 ModuleObject
->TextSection
->RosSymInfo
= NULL
;
585 /*! \brief Called when a symbol file is loaded by the loader?
587 * Tries to find a driver (.sys) or executable (.exe) with the same base name
588 * as the symbol file and sets the drivers/exes symbol info to the loaded
590 * Used to load ntoskrnl and hal symbols before the SystemRoot is available to us.
592 * \param FileName Filename for which the symbols are loaded.
595 KdbSymProcessBootSymbols(IN PCHAR FileName
)
597 PMODULE_OBJECT ModuleObject
;
598 UNICODE_STRING UnicodeString
;
599 PLOADER_MODULE KeLoaderModules
= (PLOADER_MODULE
)KeLoaderBlock
.ModsAddr
;
600 ANSI_STRING AnsiString
;
604 DPRINT("KdbSymProcessBootSymbols(%s)\n", FileName
);
606 if (0 == _stricmp(FileName
, "ntoskrnl.sym"))
608 RtlInitAnsiString(&AnsiString
, "ntoskrnl.exe");
613 RtlInitAnsiString(&AnsiString
, FileName
);
616 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
617 ModuleObject
= LdrGetModuleObject(&UnicodeString
);
618 RtlFreeUnicodeString(&UnicodeString
);
620 if (ModuleObject
!= NULL
)
624 ModuleObject
->TextSection
->RosSymInfo
= NULL
;
628 for (i
= 0; i
< KeLoaderBlock
.ModsCount
; i
++)
630 if (0 == _stricmp(FileName
, (PCHAR
)KeLoaderModules
[i
].String
))
635 if (i
< KeLoaderBlock
.ModsCount
)
637 KeLoaderModules
[i
].Reserved
= 1;
638 if (ModuleObject
->TextSection
->RosSymInfo
!= NULL
)
640 KdbpSymRemoveCachedFile(ModuleObject
->TextSection
->RosSymInfo
);
645 if (! RosSymCreateFromRaw((PVOID
) KeLoaderModules
[i
].ModStart
,
646 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
647 &ModuleObject
->TextSection
->RosSymInfo
))
654 if (! RosSymCreateFromMem((PVOID
) KeLoaderModules
[i
].ModStart
,
655 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
656 &ModuleObject
->TextSection
->RosSymInfo
))
662 /* add file to cache */
663 RtlInitAnsiString(&AnsiString
, FileName
);
664 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
665 KdbpSymAddCachedFile(&UnicodeString
, ModuleObject
->TextSection
->RosSymInfo
);
666 RtlFreeUnicodeString(&UnicodeString
);
668 DPRINT("Installed symbols: %s@%08x-%08x %p\n",
671 ModuleObject
->Length
+ ModuleObject
->Base
,
672 ModuleObject
->TextSection
->RosSymInfo
);
677 /*! \brief Initializes the KDB symbols implementation.
679 * \param NtoskrnlTextSection MODULE_TEXT_SECTION of ntoskrnl.exe
680 * \param LdrHalTextSection MODULE_TEXT_SECTION of hal.sys
683 KdbSymInit(IN PMODULE_TEXT_SECTION NtoskrnlTextSection
,
684 IN PMODULE_TEXT_SECTION LdrHalTextSection
)
690 NtoskrnlTextSection
->RosSymInfo
= NULL
;
691 LdrHalTextSection
->RosSymInfo
= NULL
;
693 InitializeListHead(&SymbolFileListHead
);
694 KeInitializeSpinLock(&SymbolFileListLock
);
702 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
703 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
704 p1
= (PCHAR
) KeLoaderBlock
.CommandLine
;
705 while('\0' != *p1
&& NULL
!= (p2
= strchr(p1
, '/')))
709 if (0 == _strnicmp(p2
, "LOADSYMBOLS", 11))
714 else if (0 == _strnicmp(p2
, "NOLOADSYMBOLS", 13))
732 YesNo
= toupper(*p2
);
733 if ('N' == YesNo
|| 'F' == YesNo
|| '0' == YesNo
)
738 LoadSymbols
= (0 < Found
);
743 RosSymInitKernelMode();