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
,
112 /*! \brief Print address...
114 * Tries to lookup line number, file name and function name for the given
115 * address and prints it.
116 * If no such information is found the address is printed in the format
117 * <module: offset>, otherwise the format will be
118 * <module: offset (filename:linenumber (functionname))>
120 * \retval TRUE Module containing \a Address was found, \a Address was printed.
121 * \retval FALSE No module containing \a Address was found, nothing was printed.
126 IN PKTRAP_FRAME Context
)
128 PLDR_DATA_TABLE_ENTRY LdrEntry
;
129 ULONG_PTR RelativeAddress
;
133 CHAR FunctionName
[256];
135 if (!KdbpSymbolsInitialized
|| !KdbpSymFindModule(Address
, NULL
, -1, &LdrEntry
))
138 RelativeAddress
= (ULONG_PTR
)Address
- (ULONG_PTR
)LdrEntry
->DllBase
;
139 Status
= KdbSymGetAddressInformation(LdrEntry
->PatchInformation
,
144 if (NT_SUCCESS(Status
))
146 DbgPrint("<%wZ:%x (%s:%d (%s))>",
147 &LdrEntry
->BaseDllName
, RelativeAddress
, FileName
, LineNumber
, FunctionName
);
151 DbgPrint("<%wZ:%x>", &LdrEntry
->BaseDllName
, RelativeAddress
);
158 /*! \brief Get information for an address (source file, line number,
161 * \param SymbolInfo Pointer to ROSSYM_INFO.
162 * \param RelativeAddress Relative address to look up.
163 * \param LineNumber Pointer to an ULONG which is filled with the line
164 * number (can be NULL)
165 * \param FileName Pointer to an array of CHARs which gets filled with
166 * the filename (can be NULL)
167 * \param FunctionName Pointer to an array of CHARs which gets filled with
168 * the function name (can be NULL)
170 * \returns NTSTATUS error code.
171 * \retval STATUS_SUCCESS At least one of the requested informations was found.
172 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
175 KdbSymGetAddressInformation(
176 IN PROSSYM_INFO RosSymInfo
,
177 IN ULONG_PTR RelativeAddress
,
178 OUT PULONG LineNumber OPTIONAL
,
179 OUT PCH FileName OPTIONAL
,
180 OUT PCH FunctionName OPTIONAL
)
182 if (!KdbpSymbolsInitialized
||
184 !RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineNumber
, FileName
, FunctionName
))
186 return STATUS_UNSUCCESSFUL
;
189 return STATUS_SUCCESS
;
192 /*! \brief Find cached symbol file.
194 * Looks through the list of cached symbol files and tries to find an already
197 * \param FileName FileName of the symbol file to look for.
199 * \returns A pointer to the cached symbol info.
200 * \retval NULL No cached info found.
202 * \sa KdbpSymAddCachedFile
205 KdbpSymFindCachedFile(
206 IN PUNICODE_STRING FileName
)
208 PIMAGE_SYMBOL_INFO_CACHE Current
;
209 PLIST_ENTRY CurrentEntry
;
212 DPRINT("Looking for cached symbol file %wZ\n", FileName
);
214 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
216 CurrentEntry
= SymbolFileListHead
.Flink
;
217 while (CurrentEntry
!= (&SymbolFileListHead
))
219 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
221 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current
->FileName
, FileName
);
222 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
225 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
226 DPRINT("Found cached file!\n");
227 return Current
->RosSymInfo
;
230 CurrentEntry
= CurrentEntry
->Flink
;
233 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
235 DPRINT("Cached file not found!\n");
239 /*! \brief Add a symbol file to the cache.
241 * \param FileName Filename of the symbol file.
242 * \param RosSymInfo Pointer to the symbol info.
244 * \sa KdbpSymRemoveCachedFile
247 KdbpSymAddCachedFile(
248 IN PUNICODE_STRING FileName
,
249 IN PROSSYM_INFO RosSymInfo
)
251 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
254 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo
);
257 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
259 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
262 CacheEntry
->FileName
.Buffer
= ExAllocatePoolWithTag(NonPagedPool
,
265 RtlCopyUnicodeString(&CacheEntry
->FileName
, FileName
);
266 ASSERT(CacheEntry
->FileName
.Buffer
);
267 CacheEntry
->RefCount
= 1;
268 CacheEntry
->RosSymInfo
= RosSymInfo
;
269 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
270 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
);
271 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
274 /*! \brief Remove a symbol file (reference) from the cache.
276 * Tries to find a cache entry matching the given symbol info and decreases
277 * it's reference count. If the refcount is 0 after decreasing it the cache
278 * entry will be removed from the list and freed.
280 * \param RosSymInfo Pointer to the symbol info.
282 * \sa KdbpSymAddCachedFile
285 KdbpSymRemoveCachedFile(
286 IN PROSSYM_INFO RosSymInfo
)
288 PIMAGE_SYMBOL_INFO_CACHE Current
;
289 PLIST_ENTRY CurrentEntry
;
292 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
294 CurrentEntry
= SymbolFileListHead
.Flink
;
295 while (CurrentEntry
!= (&SymbolFileListHead
))
297 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
299 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
301 ASSERT(Current
->RefCount
> 0);
303 if (Current
->RefCount
< 1)
305 RemoveEntryList(&Current
->ListEntry
);
306 RosSymDelete(Current
->RosSymInfo
);
310 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
314 CurrentEntry
= CurrentEntry
->Flink
;
317 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
318 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo
);
321 /*! \brief Loads a symbol file.
323 * \param FileName Filename of the symbol file to load.
324 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
326 * \sa KdbpSymUnloadModuleSymbols
329 KdbpSymLoadModuleSymbols(
330 IN PUNICODE_STRING FileName
,
331 OUT PROSSYM_INFO
*RosSymInfo
)
333 OBJECT_ATTRIBUTES ObjectAttributes
;
336 IO_STATUS_BLOCK IoStatusBlock
;
338 /* Allow KDB to break on module load */
339 KdbModuleLoaded(FileName
);
347 /* Try to find cached (already loaded) symbol file */
348 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
351 DPRINT("Found cached symbol file %wZ\n", FileName
);
356 InitializeObjectAttributes(&ObjectAttributes
,
362 DPRINT("Attempting to open image: %wZ\n", FileName
);
364 Status
= ZwOpenFile(&FileHandle
,
368 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
369 FILE_SYNCHRONOUS_IO_NONALERT
);
370 if (!NT_SUCCESS(Status
))
372 DPRINT("Could not open image file: %wZ\n", FileName
);
376 DPRINT("Loading symbols from %wZ...\n", FileName
);
378 if (!RosSymCreateFromFile(&FileHandle
, RosSymInfo
))
380 DPRINT("Failed to load symbols from %wZ\n", FileName
);
386 DPRINT("Symbols loaded.\n");
388 /* add file to cache */
389 KdbpSymAddCachedFile(FileName
, *RosSymInfo
);
391 DPRINT("Installed symbols: %wZ %p\n", FileName
, *RosSymInfo
);
395 KdbSymProcessSymbols(
396 IN PLDR_DATA_TABLE_ENTRY LdrEntry
)
400 LdrEntry
->PatchInformation
= NULL
;
404 /* Remove symbol info if it already exists */
405 if (LdrEntry
->PatchInformation
)
406 KdbpSymRemoveCachedFile(LdrEntry
->PatchInformation
);
408 /* Load new symbol information */
409 if (! RosSymCreateFromMem(LdrEntry
->DllBase
,
410 LdrEntry
->SizeOfImage
,
411 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
))
413 /* Error loading symbol info, try to load it from file */
414 KdbpSymLoadModuleSymbols(&LdrEntry
->FullDllName
,
415 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
);
417 /* It already added symbols to cache */
421 /* Add file to cache */
422 KdbpSymAddCachedFile(&LdrEntry
->FullDllName
, LdrEntry
->PatchInformation
);
425 DPRINT("Installed symbols: %wZ@%p-%p %p\n",
426 &LdrEntry
->BaseDllName
,
428 (PVOID
)(LdrEntry
->SizeOfImage
+ (ULONG_PTR
)LdrEntry
->DllBase
),
429 LdrEntry
->PatchInformation
);
443 /*! \brief Initializes the KDB symbols implementation.
445 * \param DispatchTable Pointer to the KD dispatch table
446 * \param BootPhase Phase of initialization
451 PKD_DISPATCH_TABLE DispatchTable
,
457 PLDR_DATA_TABLE_ENTRY LdrEntry
;
459 DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase
);
464 /* Load symbols only if we have 96Mb of RAM or more */
465 if (MmNumberOfPhysicalPages
>= 0x6000)
471 /* Write out the functions that we support for now */
472 DispatchTable
->KdpInitRoutine
= KdpKdbgInit
;
473 DispatchTable
->KdpPrintRoutine
= KdbDebugPrint
;
475 /* Register as a Provider */
476 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
478 /* Perform actual initialization of symbol module */
479 //NtoskrnlModuleObject->PatchInformation = NULL;
480 //LdrHalModuleObject->PatchInformation = NULL;
482 InitializeListHead(&SymbolFileListHead
);
483 KeInitializeSpinLock(&SymbolFileListLock
);
485 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
486 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
487 ASSERT(KeLoaderBlock
);
488 p1
= KeLoaderBlock
->LoadOptions
;
489 while('\0' != *p1
&& NULL
!= (p2
= strchr(p1
, '/')))
493 if (0 == _strnicmp(p2
, "LOADSYMBOLS", 11))
498 else if (0 == _strnicmp(p2
, "NOLOADSYMBOLS", 13))
516 YesNo
= toupper(*p2
);
517 if ('N' == YesNo
|| 'F' == YesNo
|| '0' == YesNo
)
522 LoadSymbols
= (0 < Found
);
527 RosSymInitKernelMode();
529 else if (BootPhase
== 1)
531 /* Load symbols for NTOSKRNL.EXE.
532 It is always the first module in PsLoadedModuleList. KeLoaderBlock can't be used here as its content is just temporary. */
533 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
534 KdbSymProcessSymbols(LdrEntry
);
536 /* Also load them for HAL.DLL. */
537 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
->Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
538 KdbSymProcessSymbols(LdrEntry
);
540 KdbpSymbolsInitialized
= TRUE
;