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)
8 * Colin Finck (colin@reactos.org)
11 /* INCLUDES *****************************************************************/
18 /* GLOBALS ******************************************************************/
20 typedef struct _IMAGE_SYMBOL_INFO_CACHE
24 UNICODE_STRING FileName
;
25 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
;
32 BOOLEAN KdbpSymbolsInitialized
= FALSE
;
34 /* FUNCTIONS ****************************************************************/
37 KdbpSymSearchModuleList(
38 IN PLIST_ENTRY current_entry
,
39 IN PLIST_ENTRY end_entry
,
44 OUT PLDR_DATA_TABLE_ENTRY
* pLdrEntry
)
46 while (current_entry
&& current_entry
!= end_entry
)
48 *pLdrEntry
= CONTAINING_RECORD(current_entry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
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
))
57 current_entry
= current_entry
->Flink
;
63 /*! \brief Find a module...
65 * \param Address If \a Address is not NULL the module containing \a Address
67 * \param Name If \a Name is not NULL the module named \a Name will be
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.
72 * \retval TRUE Module was found, \a pLdrEntry was filled.
73 * \retval FALSE No module was found.
77 IN PVOID Address OPTIONAL
,
78 IN LPCWSTR Name OPTIONAL
,
79 IN INT Index OPTIONAL
,
80 OUT PLDR_DATA_TABLE_ENTRY
* pLdrEntry
)
83 PEPROCESS CurrentProcess
;
85 /* First try to look up the module in the kernel module list. */
86 if(KdbpSymSearchModuleList(PsLoadedModuleList
.Flink
,
97 /* That didn't succeed. Try the module list of the current process now. */
98 CurrentProcess
= PsGetCurrentProcess();
100 if(!CurrentProcess
|| !CurrentProcess
->Peb
|| !CurrentProcess
->Peb
->Ldr
)
103 return KdbpSymSearchModuleList(CurrentProcess
->Peb
->Ldr
->InLoadOrderModuleList
.Flink
,
104 &CurrentProcess
->Peb
->Ldr
->InLoadOrderModuleList
,
114 KdbpSymUnicodeToAnsi(IN PUNICODE_STRING Unicode
,
122 /* Set length and normalize it */
123 i
= Unicode
->Length
/ sizeof(WCHAR
);
124 i
= min(i
, Length
- 1);
126 /* Set source and destination, and copy */
127 pw
= Unicode
->Buffer
;
129 while (i
--) *p
++ = (CHAR
)*pw
++;
131 /* Null terminate and return */
136 /*! \brief Print address...
138 * Tries to lookup line number, file name and function name for the given
139 * address and prints it.
140 * If no such information is found the address is printed in the format
141 * <module: offset>, otherwise the format will be
142 * <module: offset (filename:linenumber (functionname))>
144 * \retval TRUE Module containing \a Address was found, \a Address was printed.
145 * \retval FALSE No module containing \a Address was found, nothing was printed.
150 IN PKTRAP_FRAME Context
)
152 PLDR_DATA_TABLE_ENTRY LdrEntry
;
153 ULONG_PTR RelativeAddress
;
157 CHAR FunctionName
[256];
158 CHAR ModuleNameAnsi
[64];
160 if (!KdbpSymbolsInitialized
|| !KdbpSymFindModule(Address
, NULL
, -1, &LdrEntry
))
163 KdbpSymUnicodeToAnsi(&LdrEntry
->BaseDllName
,
165 sizeof(ModuleNameAnsi
));
167 RelativeAddress
= (ULONG_PTR
)Address
- (ULONG_PTR
)LdrEntry
->DllBase
;
168 Status
= KdbSymGetAddressInformation(LdrEntry
->PatchInformation
,
173 if (NT_SUCCESS(Status
))
175 DbgPrint("<%s:%x (%s:%d (%s))>",
176 ModuleNameAnsi
, RelativeAddress
, FileName
, LineNumber
, FunctionName
);
180 DbgPrint("<%s:%x>", ModuleNameAnsi
, RelativeAddress
);
187 /*! \brief Get information for an address (source file, line number,
190 * \param SymbolInfo Pointer to ROSSYM_INFO.
191 * \param RelativeAddress Relative address to look up.
192 * \param LineNumber Pointer to an ULONG which is filled with the line
193 * number (can be NULL)
194 * \param FileName Pointer to an array of CHARs which gets filled with
195 * the filename (can be NULL)
196 * \param FunctionName Pointer to an array of CHARs which gets filled with
197 * the function name (can be NULL)
199 * \returns NTSTATUS error code.
200 * \retval STATUS_SUCCESS At least one of the requested informations was found.
201 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
204 KdbSymGetAddressInformation(
205 IN PROSSYM_INFO RosSymInfo
,
206 IN ULONG_PTR RelativeAddress
,
207 OUT PULONG LineNumber OPTIONAL
,
208 OUT PCH FileName OPTIONAL
,
209 OUT PCH FunctionName OPTIONAL
)
211 if (!KdbpSymbolsInitialized
||
213 !RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineNumber
, FileName
, FunctionName
))
215 return STATUS_UNSUCCESSFUL
;
218 return STATUS_SUCCESS
;
221 /*! \brief Find cached symbol file.
223 * Looks through the list of cached symbol files and tries to find an already
226 * \param FileName FileName of the symbol file to look for.
228 * \returns A pointer to the cached symbol info.
229 * \retval NULL No cached info found.
231 * \sa KdbpSymAddCachedFile
234 KdbpSymFindCachedFile(
235 IN PUNICODE_STRING FileName
)
237 PIMAGE_SYMBOL_INFO_CACHE Current
;
238 PLIST_ENTRY CurrentEntry
;
241 DPRINT("Looking for cached symbol file %wZ\n", FileName
);
243 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
245 CurrentEntry
= SymbolFileListHead
.Flink
;
246 while (CurrentEntry
!= (&SymbolFileListHead
))
248 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
250 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current
->FileName
, FileName
);
251 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
254 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
255 DPRINT("Found cached file!\n");
256 return Current
->RosSymInfo
;
259 CurrentEntry
= CurrentEntry
->Flink
;
262 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
264 DPRINT("Cached file not found!\n");
268 /*! \brief Add a symbol file to the cache.
270 * \param FileName Filename of the symbol file.
271 * \param RosSymInfo Pointer to the symbol info.
273 * \sa KdbpSymRemoveCachedFile
276 KdbpSymAddCachedFile(
277 IN PUNICODE_STRING FileName
,
278 IN PROSSYM_INFO RosSymInfo
)
280 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
283 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo
);
286 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
288 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
291 CacheEntry
->FileName
.Buffer
= ExAllocatePoolWithTag(NonPagedPool
,
294 RtlCopyUnicodeString(&CacheEntry
->FileName
, FileName
);
295 ASSERT(CacheEntry
->FileName
.Buffer
);
296 CacheEntry
->RefCount
= 1;
297 CacheEntry
->RosSymInfo
= RosSymInfo
;
298 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
299 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
);
300 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
303 /*! \brief Remove a symbol file (reference) from the cache.
305 * Tries to find a cache entry matching the given symbol info and decreases
306 * it's reference count. If the refcount is 0 after decreasing it the cache
307 * entry will be removed from the list and freed.
309 * \param RosSymInfo Pointer to the symbol info.
311 * \sa KdbpSymAddCachedFile
314 KdbpSymRemoveCachedFile(
315 IN PROSSYM_INFO RosSymInfo
)
317 PIMAGE_SYMBOL_INFO_CACHE Current
;
318 PLIST_ENTRY CurrentEntry
;
321 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
323 CurrentEntry
= SymbolFileListHead
.Flink
;
324 while (CurrentEntry
!= (&SymbolFileListHead
))
326 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
328 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
330 ASSERT(Current
->RefCount
> 0);
332 if (Current
->RefCount
< 1)
334 RemoveEntryList(&Current
->ListEntry
);
335 RosSymDelete(Current
->RosSymInfo
);
339 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
343 CurrentEntry
= CurrentEntry
->Flink
;
346 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
347 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo
);
350 /*! \brief Loads a symbol file.
352 * \param FileName Filename of the symbol file to load.
353 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
355 * \sa KdbpSymUnloadModuleSymbols
358 KdbpSymLoadModuleSymbols(
359 IN PUNICODE_STRING FileName
,
360 OUT PROSSYM_INFO
*RosSymInfo
)
362 OBJECT_ATTRIBUTES ObjectAttributes
;
365 IO_STATUS_BLOCK IoStatusBlock
;
367 /* Allow KDB to break on module load */
368 KdbModuleLoaded(FileName
);
376 /* Try to find cached (already loaded) symbol file */
377 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
380 DPRINT("Found cached symbol file %wZ\n", FileName
);
385 InitializeObjectAttributes(&ObjectAttributes
,
391 DPRINT("Attempting to open image: %wZ\n", FileName
);
393 Status
= ZwOpenFile(&FileHandle
,
394 FILE_READ_ACCESS
| SYNCHRONIZE
,
397 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
398 FILE_SYNCHRONOUS_IO_NONALERT
);
399 if (!NT_SUCCESS(Status
))
401 DPRINT("Could not open image file: %wZ\n", FileName
);
405 DPRINT("Loading symbols from %wZ...\n", FileName
);
407 if (!RosSymCreateFromFile(&FileHandle
, RosSymInfo
))
409 DPRINT("Failed to load symbols from %wZ\n", FileName
);
415 DPRINT("Symbols loaded.\n");
417 /* add file to cache */
418 KdbpSymAddCachedFile(FileName
, *RosSymInfo
);
420 DPRINT("Installed symbols: %wZ %p\n", FileName
, *RosSymInfo
);
424 KdbSymProcessSymbols(
425 IN PLDR_DATA_TABLE_ENTRY LdrEntry
)
429 LdrEntry
->PatchInformation
= NULL
;
433 /* Remove symbol info if it already exists */
434 if (LdrEntry
->PatchInformation
)
435 KdbpSymRemoveCachedFile(LdrEntry
->PatchInformation
);
437 /* Load new symbol information */
438 if (! RosSymCreateFromMem(LdrEntry
->DllBase
,
439 LdrEntry
->SizeOfImage
,
440 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
))
442 /* Error loading symbol info, try to load it from file */
443 KdbpSymLoadModuleSymbols(&LdrEntry
->FullDllName
,
444 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
);
446 /* It already added symbols to cache */
450 /* Add file to cache */
451 KdbpSymAddCachedFile(&LdrEntry
->FullDllName
, LdrEntry
->PatchInformation
);
454 DPRINT("Installed symbols: %wZ@%p-%p %p\n",
455 &LdrEntry
->BaseDllName
,
457 (PVOID
)(LdrEntry
->SizeOfImage
+ (ULONG_PTR
)LdrEntry
->DllBase
),
458 LdrEntry
->PatchInformation
);
472 /*! \brief Initializes the KDB symbols implementation.
474 * \param DispatchTable Pointer to the KD dispatch table
475 * \param BootPhase Phase of initialization
480 PKD_DISPATCH_TABLE DispatchTable
,
486 PLDR_DATA_TABLE_ENTRY LdrEntry
;
488 DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase
);
493 /* Load symbols only if we have 96Mb of RAM or more */
494 if (MmNumberOfPhysicalPages
>= 0x6000)
500 /* Write out the functions that we support for now */
501 DispatchTable
->KdpInitRoutine
= KdpKdbgInit
;
502 DispatchTable
->KdpPrintRoutine
= KdbDebugPrint
;
504 /* Register as a Provider */
505 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
507 /* Perform actual initialization of symbol module */
508 //NtoskrnlModuleObject->PatchInformation = NULL;
509 //LdrHalModuleObject->PatchInformation = NULL;
511 InitializeListHead(&SymbolFileListHead
);
512 KeInitializeSpinLock(&SymbolFileListLock
);
514 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
515 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
516 ASSERT(KeLoaderBlock
);
517 p1
= KeLoaderBlock
->LoadOptions
;
518 while('\0' != *p1
&& NULL
!= (p2
= strchr(p1
, '/')))
522 if (0 == _strnicmp(p2
, "LOADSYMBOLS", 11))
527 else if (0 == _strnicmp(p2
, "NOLOADSYMBOLS", 13))
545 YesNo
= toupper(*p2
);
546 if ('N' == YesNo
|| 'F' == YesNo
|| '0' == YesNo
)
551 LoadSymbols
= (0 < Found
);
556 RosSymInitKernelMode();
558 else if (BootPhase
== 1)
560 /* Load symbols for NTOSKRNL.EXE.
561 It is always the first module in PsLoadedModuleList. KeLoaderBlock can't be used here as its content is just temporary. */
562 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
563 KdbSymProcessSymbols(LdrEntry
);
565 /* Also load them for HAL.DLL. */
566 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
->Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
567 KdbSymProcessSymbols(LdrEntry
);
569 KdbpSymbolsInitialized
= TRUE
;