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>
14 #include <internal/ntoskrnl.h>
15 #include <internal/ke.h>
16 #include <internal/i386/segment.h>
17 #include <internal/i386/mm.h>
18 #include <internal/module.h>
19 #include <internal/mm.h>
20 #include <internal/ps.h>
21 #include <internal/trap.h>
22 #include <ntdll/ldr.h>
23 #include <internal/safe.h>
24 #include <internal/kd.h>
25 #include <rosrtl/string.h>
26 #include <reactos/rossym.h>
29 #include <internal/debug.h>
31 #include <internal/kdb.h>
33 /* GLOBALS ******************************************************************/
35 #define TAG_KDBS TAG('K', 'D', 'B', 'S')
37 typedef struct _IMAGE_SYMBOL_INFO_CACHE
{
40 UNICODE_STRING FileName
;
41 PROSSYM_INFO RosSymInfo
;
42 } IMAGE_SYMBOL_INFO_CACHE
, *PIMAGE_SYMBOL_INFO_CACHE
;
44 static LIST_ENTRY SymbolFileListHead
;
45 static KSPIN_LOCK SymbolFileListLock
;
48 /* FUNCTIONS ****************************************************************/
50 /*! \brief Find a user-mode module...
52 * \param Address If \a Address is not NULL the module containing \a Address
54 * \param Name If \a Name is not NULL the module named \a Name will be
56 * \param Index If \a Index is >= 0 the Index'th module will be returned.
57 * \param pInfo Pointer to a KDB_MODULE_INFO which is filled.
59 * \retval TRUE Module was found, \a pInfo was filled.
60 * \retval FALSE No module was found.
62 * \sa KdbpSymFindModule
65 KdbpSymFindUserModule(IN PVOID Address OPTIONAL
,
66 IN LPCWSTR Name OPTIONAL
,
67 IN INT Index OPTIONAL
,
68 OUT PKDB_MODULE_INFO pInfo
)
70 PLIST_ENTRY current_entry
;
72 PEPROCESS CurrentProcess
;
76 CurrentProcess
= PsGetCurrentProcess();
77 if (CurrentProcess
!= NULL
)
79 Peb
= CurrentProcess
->Peb
;
87 current_entry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
89 while (current_entry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
90 current_entry
!= NULL
)
92 current
= CONTAINING_RECORD(current_entry
, LDR_MODULE
, InLoadOrderModuleList
);
94 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->BaseAddress
&&
95 Address
< (PVOID
)((char *)current
->BaseAddress
+ current
->ResidentSize
))) ||
96 (Name
!= NULL
&& _wcsicmp(current
->BaseDllName
.Buffer
, Name
) == 0) ||
97 (Index
>= 0 && Count
++ == Index
))
99 INT Length
= current
->BaseDllName
.Length
;
102 wcsncpy(pInfo
->Name
, current
->BaseDllName
.Buffer
, Length
);
103 pInfo
->Name
[Length
] = L
'\0';
104 pInfo
->Base
= (ULONG_PTR
)current
->BaseAddress
;
105 pInfo
->Size
= current
->ResidentSize
;
106 pInfo
->RosSymInfo
= current
->RosSymInfo
;
109 current_entry
= current_entry
->Flink
;
115 /*! \brief Find a kernel-mode module...
117 * Works like \a KdbpSymFindUserModule.
119 * \sa KdbpSymFindUserModule
122 KdbpSymFindModule(IN PVOID Address OPTIONAL
,
123 IN LPCWSTR Name OPTIONAL
,
124 IN INT Index OPTIONAL
,
125 OUT PKDB_MODULE_INFO pInfo
)
127 PLIST_ENTRY current_entry
;
128 MODULE_TEXT_SECTION
* current
;
129 extern LIST_ENTRY ModuleTextListHead
;
132 current_entry
= ModuleTextListHead
.Flink
;
134 while (current_entry
!= &ModuleTextListHead
&&
135 current_entry
!= NULL
)
137 current
= CONTAINING_RECORD(current_entry
, MODULE_TEXT_SECTION
, ListEntry
);
139 if ((Address
!= NULL
&& (Address
>= (PVOID
)current
->Base
&&
140 Address
< (PVOID
)(current
->Base
+ current
->Length
))) ||
141 (Name
!= NULL
&& _wcsicmp(current
->Name
, Name
) == 0) ||
142 (Index
>= 0 && Count
++ == Index
))
144 wcsncpy(pInfo
->Name
, current
->Name
, 255);
145 pInfo
->Name
[255] = L
'\0';
146 pInfo
->Base
= (ULONG_PTR
)current
->Base
;
147 pInfo
->Size
= current
->Length
;
148 pInfo
->RosSymInfo
= current
->RosSymInfo
;
151 current_entry
= current_entry
->Flink
;
154 return KdbpSymFindUserModule(Address
, Name
, Index
-Count
, pInfo
);
157 /*! \brief Find module by address...
159 * \param Address Any address inside the module to look for.
160 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
163 * \retval TRUE Success - module found.
164 * \retval FALSE Failure - module not found.
166 * \sa KdbpSymFindModuleByName
167 * \sa KdbpSymFindModuleByIndex
170 KdbpSymFindModuleByAddress(IN PVOID Address
,
171 OUT PKDB_MODULE_INFO pInfo
)
173 return KdbpSymFindModule(Address
, NULL
, -1, pInfo
);
176 /*! \brief Find module by name...
178 * \param Name Name of the module to look for.
179 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
182 * \retval TRUE Success - module found.
183 * \retval FALSE Failure - module not found.
185 * \sa KdbpSymFindModuleByAddress
186 * \sa KdbpSymFindModuleByIndex
189 KdbpSymFindModuleByName(IN LPCWSTR Name
,
190 OUT PKDB_MODULE_INFO pInfo
)
192 return KdbpSymFindModule(NULL
, Name
, -1, pInfo
);
195 /*! \brief Find module by index...
197 * \param Index Index of the module to return.
198 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
201 * \retval TRUE Success - module found.
202 * \retval FALSE Failure - module not found.
204 * \sa KdbpSymFindModuleByName
205 * \sa KdbpSymFindModuleByAddress
208 KdbpSymFindModuleByIndex(IN INT Index
,
209 OUT PKDB_MODULE_INFO pInfo
)
211 return KdbpSymFindModule(NULL
, NULL
, Index
, pInfo
);
214 /*! \brief Print address...
216 * Tries to lookup line number, file name and function name for the given
217 * address and prints it.
218 * If no such information is found the address is printed in the format
219 * <module: offset>, otherwise the format will be
220 * <module: offset (filename:linenumber (functionname))>
222 * \retval TRUE Module containing \a Address was found, \a Address was printed.
223 * \retval FALSE No module containing \a Address was found, nothing was printed.
226 KdbSymPrintAddress(IN PVOID Address
)
228 KDB_MODULE_INFO Info
;
229 ULONG_PTR RelativeAddress
;
233 CHAR FunctionName
[256];
235 if (!KdbpSymFindModuleByAddress(Address
, &Info
))
238 RelativeAddress
= (ULONG_PTR
) Address
- Info
.Base
;
239 Status
= KdbSymGetAddressInformation(Info
.RosSymInfo
,
244 if (NT_SUCCESS(Status
))
246 DbgPrint("<%ws:%x (%s:%d (%s))>",
247 Info
.Name
, RelativeAddress
, FileName
, LineNumber
, FunctionName
);
251 DbgPrint("<%ws:%x>", Info
.Name
, RelativeAddress
);
258 /*! \brief Get information for an address (source file, line number,
261 * \param SymbolInfo Pointer to ROSSYM_INFO.
262 * \param RelativeAddress Relative address to look up.
263 * \param LineNumber Pointer to an ULONG which is filled with the line
264 * number (can be NULL)
265 * \param FileName Pointer to an array of CHARs which gets filled with
266 * the filename (can be NULL)
267 * \param FunctionName Pointer to an array of CHARs which gets filled with
268 * the function name (can be NULL)
270 * \returns NTSTATUS error code.
271 * \retval STATUS_SUCCESS At least one of the requested informations was found.
272 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
275 KdbSymGetAddressInformation(IN PROSSYM_INFO RosSymInfo
,
276 IN ULONG_PTR RelativeAddress
,
277 OUT PULONG LineNumber OPTIONAL
,
278 OUT PCH FileName OPTIONAL
,
279 OUT PCH FunctionName OPTIONAL
)
281 if (NULL
== RosSymInfo
)
283 return STATUS_UNSUCCESSFUL
;
286 if (! RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineNumber
,
287 FileName
, FunctionName
))
289 return STATUS_UNSUCCESSFUL
;
292 return STATUS_SUCCESS
;
295 /*! \brief Find cached symbol file.
297 * Looks through the list of cached symbol files and tries to find an already
300 * \param FileName FileName of the symbol file to look for.
302 * \returns A pointer to the cached symbol info.
303 * \retval NULL No cached info found.
305 * \sa KdbpSymAddCachedFile
308 KdbpSymFindCachedFile(IN PUNICODE_STRING FileName
)
310 PIMAGE_SYMBOL_INFO_CACHE Current
;
311 PLIST_ENTRY CurrentEntry
;
314 DPRINT("Looking for cached symbol file %wZ\n", FileName
);
316 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
318 CurrentEntry
= SymbolFileListHead
.Flink
;
319 while (CurrentEntry
!= (&SymbolFileListHead
))
321 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
323 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current
->FileName
, FileName
);
324 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
327 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
328 DPRINT("Found cached file!\n");
329 return Current
->RosSymInfo
;
332 CurrentEntry
= CurrentEntry
->Flink
;
335 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
337 DPRINT("Cached file not found!\n");
341 /*! \brief Add a symbol file to the cache.
343 * \param FileName Filename of the symbol file.
344 * \param RosSymInfo Pointer to the symbol info.
346 * \sa KdbpSymRemoveCachedFile
349 KdbpSymAddCachedFile(IN PUNICODE_STRING FileName
,
350 IN PROSSYM_INFO RosSymInfo
)
352 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
354 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo
);
357 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
359 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
362 RtlpCreateUnicodeString(&CacheEntry
->FileName
, FileName
->Buffer
, PagedPool
);
363 ASSERT(CacheEntry
->FileName
.Buffer
);
364 CacheEntry
->RefCount
= 1;
365 CacheEntry
->RosSymInfo
= RosSymInfo
;
366 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
); /* FIXME: Lock list? */
369 /*! \brief Remove a symbol file (reference) from the cache.
371 * Tries to find a cache entry matching the given symbol info and decreases
372 * it's reference count. If the refcount is 0 after decreasing it the cache
373 * entry will be removed from the list and freed.
375 * \param RosSymInfo Pointer to the symbol info.
377 * \sa KdbpSymAddCachedFile
380 KdbpSymRemoveCachedFile(IN PROSSYM_INFO RosSymInfo
)
382 PIMAGE_SYMBOL_INFO_CACHE Current
;
383 PLIST_ENTRY CurrentEntry
;
386 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
388 CurrentEntry
= SymbolFileListHead
.Flink
;
389 while (CurrentEntry
!= (&SymbolFileListHead
))
391 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
393 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
395 ASSERT(Current
->RefCount
> 0);
397 if (Current
->RefCount
< 1)
399 RemoveEntryList(&Current
->ListEntry
);
400 RosSymDelete(Current
->RosSymInfo
);
403 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
407 CurrentEntry
= CurrentEntry
->Flink
;
410 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
411 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo
);
414 /*! \brief Loads a symbol file.
416 * \param FileName Filename of the symbol file to load.
417 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
419 * \sa KdbpSymUnloadModuleSymbols
422 KdbpSymLoadModuleSymbols(IN PUNICODE_STRING FileName
,
423 OUT PROSSYM_INFO
*RosSymInfo
)
425 OBJECT_ATTRIBUTES ObjectAttributes
;
428 IO_STATUS_BLOCK IoStatusBlock
;
431 /* Allow KDB to break on module load */
432 KdbModuleLoaded(FileName
);
435 /* Try to find cached (already loaded) symbol file */
436 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
437 if (*RosSymInfo
!= NULL
)
439 DPRINT("Found cached symbol file %wZ\n", FileName
);
444 InitializeObjectAttributes(&ObjectAttributes
,
450 DPRINT("Attempting to open image: %wZ\n", FileName
);
452 Status
= ZwOpenFile(&FileHandle
,
456 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
457 FILE_SYNCHRONOUS_IO_NONALERT
|FILE_NO_INTERMEDIATE_BUFFERING
);
458 if (!NT_SUCCESS(Status
))
460 DPRINT("Could not open image file: %wZ\n", &FileName
);
464 DPRINT("Loading symbols from %wZ...\n", FileName
);
466 if (! RosSymCreateFromFile(&FileHandle
, RosSymInfo
))
468 DPRINT("Failed to load symbols from %wZ\n", FileName
);
474 DPRINT("Symbols loaded.\n");
476 /* add file to cache */
477 KdbpSymAddCachedFile(FileName
, *RosSymInfo
);
479 DPRINT("Installed symbols: %wZ %p\n", FileName
, *RosSymInfo
);
482 /*! \brief Unloads symbol info.
484 * \param RosSymInfo Pointer to the symbol info to unload.
486 * \sa KdbpSymLoadModuleSymbols
489 KdbpSymUnloadModuleSymbols(IN PROSSYM_INFO RosSymInfo
)
491 DPRINT("Unloading symbols\n");
493 if (RosSymInfo
!= NULL
)
495 KdbpSymRemoveCachedFile(RosSymInfo
);
499 /*! \brief Load symbol info for a user module.
501 * \param LdrModule Pointer to the module to load symbols for.
504 KdbSymLoadUserModuleSymbols(IN PLDR_MODULE LdrModule
)
506 static WCHAR Prefix
[] = L
"\\??\\";
507 UNICODE_STRING KernelName
;
508 DPRINT("LdrModule %p\n", LdrModule
);
510 KernelName
.MaximumLength
= sizeof(Prefix
) + LdrModule
->FullDllName
.Length
;
511 KernelName
.Length
= KernelName
.MaximumLength
- sizeof(WCHAR
);
512 KernelName
.Buffer
= ExAllocatePoolWithTag(PagedPool
, KernelName
.MaximumLength
, TAG_KDBS
);
513 if (NULL
== KernelName
.Buffer
)
517 memcpy(KernelName
.Buffer
, Prefix
, sizeof(Prefix
) - sizeof(WCHAR
));
518 memcpy(KernelName
.Buffer
+ sizeof(Prefix
) / sizeof(WCHAR
) - 1, LdrModule
->FullDllName
.Buffer
,
519 LdrModule
->FullDllName
.Length
);
520 KernelName
.Buffer
[KernelName
.Length
/ sizeof(WCHAR
)] = L
'\0';
522 LdrModule
->RosSymInfo
= NULL
;
524 KdbpSymLoadModuleSymbols(&KernelName
, &LdrModule
->RosSymInfo
);
526 ExFreePool(KernelName
.Buffer
);
529 /*! \brief Frees all symbols loaded for a process.
531 * \param Process Pointer to a process.
534 KdbSymFreeProcessSymbols(IN PEPROCESS Process
)
536 PLIST_ENTRY CurrentEntry
;
538 PEPROCESS CurrentProcess
;
541 CurrentProcess
= PsGetCurrentProcess();
542 if (CurrentProcess
!= Process
)
544 KeAttachProcess(EPROCESS_TO_KPROCESS(Process
));
550 CurrentEntry
= Peb
->Ldr
->InLoadOrderModuleList
.Flink
;
551 while (CurrentEntry
!= &Peb
->Ldr
->InLoadOrderModuleList
&&
552 CurrentEntry
!= NULL
)
554 Current
= CONTAINING_RECORD(CurrentEntry
, LDR_MODULE
, InLoadOrderModuleList
);
556 KdbpSymUnloadModuleSymbols(Current
->RosSymInfo
);
558 CurrentEntry
= CurrentEntry
->Flink
;
560 if (CurrentProcess
!= Process
)
566 /*! \brief Load symbol info for a driver.
568 * \param Filename Filename of the driver.
569 * \param Module Pointer to the driver MODULE_OBJECT.
572 KdbSymLoadDriverSymbols(IN PUNICODE_STRING Filename
,
573 IN PMODULE_OBJECT Module
)
575 /* Load symbols for the image if available */
576 DPRINT("Loading driver %wZ symbols (driver @ %08x)\n", Filename
, Module
->Base
);
578 Module
->TextSection
->RosSymInfo
= NULL
;
580 KdbpSymLoadModuleSymbols(Filename
, &Module
->TextSection
->RosSymInfo
);
583 /*! \brief Unloads symbol info for a driver.
585 * \param ModuleObject Pointer to the driver MODULE_OBJECT.
588 KdbSymUnloadDriverSymbols(IN PMODULE_OBJECT ModuleObject
)
590 /* Unload symbols for module if available */
591 KdbpSymUnloadModuleSymbols(ModuleObject
->TextSection
->RosSymInfo
);
592 ModuleObject
->TextSection
->RosSymInfo
= NULL
;
595 /*! \brief Called when a symbol file is loaded by the loader?
597 * Tries to find a driver (.sys) or executable (.exe) with the same base name
598 * as the symbol file and sets the drivers/exes symbol info to the loaded
600 * Used to load ntoskrnl and hal symbols before the SystemRoot is available to us.
602 * \param FileName Filename for which the symbols are loaded.
605 KdbSymProcessBootSymbols(IN PCHAR FileName
)
607 PMODULE_OBJECT ModuleObject
;
608 UNICODE_STRING UnicodeString
;
609 PLOADER_MODULE KeLoaderModules
= (PLOADER_MODULE
)KeLoaderBlock
.ModsAddr
;
610 ANSI_STRING AnsiString
;
614 DPRINT("KdbSymProcessBootSymbols(%s)\n", FileName
);
616 if (0 == _stricmp(FileName
, "ntoskrnl.sym"))
618 RtlInitAnsiString(&AnsiString
, "ntoskrnl.exe");
623 RtlInitAnsiString(&AnsiString
, FileName
);
626 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
627 ModuleObject
= LdrGetModuleObject(&UnicodeString
);
628 RtlFreeUnicodeString(&UnicodeString
);
630 if (ModuleObject
!= NULL
)
632 for (i
= 0; i
< KeLoaderBlock
.ModsCount
; i
++)
634 if (0 == _stricmp(FileName
, (PCHAR
)KeLoaderModules
[i
].String
))
639 if (i
< KeLoaderBlock
.ModsCount
)
641 KeLoaderModules
[i
].Reserved
= 1;
642 if (ModuleObject
->TextSection
->RosSymInfo
!= NULL
)
644 KdbpSymRemoveCachedFile(ModuleObject
->TextSection
->RosSymInfo
);
649 if (! RosSymCreateFromRaw((PVOID
) KeLoaderModules
[i
].ModStart
,
650 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
651 &ModuleObject
->TextSection
->RosSymInfo
))
658 if (! RosSymCreateFromMem((PVOID
) KeLoaderModules
[i
].ModStart
,
659 KeLoaderModules
[i
].ModEnd
- KeLoaderModules
[i
].ModStart
,
660 &ModuleObject
->TextSection
->RosSymInfo
))
666 /* add file to cache */
667 RtlInitAnsiString(&AnsiString
, FileName
);
668 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, TRUE
);
669 KdbpSymAddCachedFile(&UnicodeString
, ModuleObject
->TextSection
->RosSymInfo
);
670 RtlFreeUnicodeString(&UnicodeString
);
672 DPRINT("Installed symbols: %s@%08x-%08x %p\n",
675 ModuleObject
->Length
+ ModuleObject
->Base
,
676 ModuleObject
->TextSection
->RosSymInfo
);
681 /*! \brief Initializes the KDB symbols implementation.
683 * \param NtoskrnlTextSection MODULE_TEXT_SECTION of ntoskrnl.exe
684 * \param LdrHalTextSection MODULE_TEXT_SECTION of hal.sys
687 KdbSymInit(IN PMODULE_TEXT_SECTION NtoskrnlTextSection
,
688 IN PMODULE_TEXT_SECTION LdrHalTextSection
)
690 NtoskrnlTextSection
->RosSymInfo
= NULL
;
691 LdrHalTextSection
->RosSymInfo
= NULL
;
693 InitializeListHead(&SymbolFileListHead
);
694 KeInitializeSpinLock(&SymbolFileListLock
);
696 RosSymInitKernelMode();