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.
127 PLDR_DATA_TABLE_ENTRY LdrEntry
;
128 ULONG_PTR RelativeAddress
;
132 CHAR FunctionName
[256];
134 if (!KdbpSymbolsInitialized
|| !KdbpSymFindModule(Address
, NULL
, -1, &LdrEntry
))
137 RelativeAddress
= (ULONG_PTR
)Address
- (ULONG_PTR
)LdrEntry
->DllBase
;
138 Status
= KdbSymGetAddressInformation(LdrEntry
->PatchInformation
,
143 if (NT_SUCCESS(Status
))
145 DbgPrint("<%wZ:%x (%s:%d (%s))>",
146 &LdrEntry
->BaseDllName
, RelativeAddress
, FileName
, LineNumber
, FunctionName
);
150 DbgPrint("<%wZ:%x>", &LdrEntry
->BaseDllName
, RelativeAddress
);
157 /*! \brief Get information for an address (source file, line number,
160 * \param SymbolInfo Pointer to ROSSYM_INFO.
161 * \param RelativeAddress Relative address to look up.
162 * \param LineNumber Pointer to an ULONG which is filled with the line
163 * number (can be NULL)
164 * \param FileName Pointer to an array of CHARs which gets filled with
165 * the filename (can be NULL)
166 * \param FunctionName Pointer to an array of CHARs which gets filled with
167 * the function name (can be NULL)
169 * \returns NTSTATUS error code.
170 * \retval STATUS_SUCCESS At least one of the requested informations was found.
171 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
174 KdbSymGetAddressInformation(
175 IN PROSSYM_INFO RosSymInfo
,
176 IN ULONG_PTR RelativeAddress
,
177 OUT PULONG LineNumber OPTIONAL
,
178 OUT PCH FileName OPTIONAL
,
179 OUT PCH FunctionName OPTIONAL
)
181 if (!KdbpSymbolsInitialized
||
183 !RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineNumber
, FileName
, FunctionName
))
185 return STATUS_UNSUCCESSFUL
;
188 return STATUS_SUCCESS
;
191 /*! \brief Find cached symbol file.
193 * Looks through the list of cached symbol files and tries to find an already
196 * \param FileName FileName of the symbol file to look for.
198 * \returns A pointer to the cached symbol info.
199 * \retval NULL No cached info found.
201 * \sa KdbpSymAddCachedFile
204 KdbpSymFindCachedFile(
205 IN PUNICODE_STRING FileName
)
207 PIMAGE_SYMBOL_INFO_CACHE Current
;
208 PLIST_ENTRY CurrentEntry
;
211 DPRINT("Looking for cached symbol file %wZ\n", FileName
);
213 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
215 CurrentEntry
= SymbolFileListHead
.Flink
;
216 while (CurrentEntry
!= (&SymbolFileListHead
))
218 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
220 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current
->FileName
, FileName
);
221 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
224 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
225 DPRINT("Found cached file!\n");
226 return Current
->RosSymInfo
;
229 CurrentEntry
= CurrentEntry
->Flink
;
232 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
234 DPRINT("Cached file not found!\n");
238 /*! \brief Add a symbol file to the cache.
240 * \param FileName Filename of the symbol file.
241 * \param RosSymInfo Pointer to the symbol info.
243 * \sa KdbpSymRemoveCachedFile
246 KdbpSymAddCachedFile(
247 IN PUNICODE_STRING FileName
,
248 IN PROSSYM_INFO RosSymInfo
)
250 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
252 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo
);
255 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
257 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
260 CacheEntry
->FileName
.Buffer
= ExAllocatePoolWithTag(NonPagedPool
,
263 RtlCopyUnicodeString(&CacheEntry
->FileName
, FileName
);
264 ASSERT(CacheEntry
->FileName
.Buffer
);
265 CacheEntry
->RefCount
= 1;
266 CacheEntry
->RosSymInfo
= RosSymInfo
;
267 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
); /* FIXME: Lock list? */
270 /*! \brief Remove a symbol file (reference) from the cache.
272 * Tries to find a cache entry matching the given symbol info and decreases
273 * it's reference count. If the refcount is 0 after decreasing it the cache
274 * entry will be removed from the list and freed.
276 * \param RosSymInfo Pointer to the symbol info.
278 * \sa KdbpSymAddCachedFile
281 KdbpSymRemoveCachedFile(
282 IN PROSSYM_INFO RosSymInfo
)
284 PIMAGE_SYMBOL_INFO_CACHE Current
;
285 PLIST_ENTRY CurrentEntry
;
288 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
290 CurrentEntry
= SymbolFileListHead
.Flink
;
291 while (CurrentEntry
!= (&SymbolFileListHead
))
293 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
295 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
297 ASSERT(Current
->RefCount
> 0);
299 if (Current
->RefCount
< 1)
301 RemoveEntryList(&Current
->ListEntry
);
302 RosSymDelete(Current
->RosSymInfo
);
306 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
310 CurrentEntry
= CurrentEntry
->Flink
;
313 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
314 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo
);
317 /*! \brief Loads a symbol file.
319 * \param FileName Filename of the symbol file to load.
320 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
322 * \sa KdbpSymUnloadModuleSymbols
325 KdbpSymLoadModuleSymbols(
326 IN PUNICODE_STRING FileName
,
327 OUT PROSSYM_INFO
*RosSymInfo
)
329 OBJECT_ATTRIBUTES ObjectAttributes
;
332 IO_STATUS_BLOCK IoStatusBlock
;
334 /* Allow KDB to break on module load */
335 KdbModuleLoaded(FileName
);
343 /* Try to find cached (already loaded) symbol file */
344 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
347 DPRINT("Found cached symbol file %wZ\n", FileName
);
352 InitializeObjectAttributes(&ObjectAttributes
,
358 DPRINT("Attempting to open image: %wZ\n", FileName
);
360 Status
= ZwOpenFile(&FileHandle
,
364 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
365 FILE_SYNCHRONOUS_IO_NONALERT
);
366 if (!NT_SUCCESS(Status
))
368 DPRINT("Could not open image file: %wZ\n", FileName
);
372 DPRINT("Loading symbols from %wZ...\n", FileName
);
374 if (!RosSymCreateFromFile(&FileHandle
, RosSymInfo
))
376 DPRINT("Failed to load symbols from %wZ\n", FileName
);
382 DPRINT("Symbols loaded.\n");
384 /* add file to cache */
385 KdbpSymAddCachedFile(FileName
, *RosSymInfo
);
387 DPRINT("Installed symbols: %wZ %p\n", FileName
, *RosSymInfo
);
391 KdbSymProcessSymbols(
392 IN PLDR_DATA_TABLE_ENTRY LdrEntry
)
396 LdrEntry
->PatchInformation
= NULL
;
400 /* Remove symbol info if it already exists */
401 if (LdrEntry
->PatchInformation
)
402 KdbpSymRemoveCachedFile(LdrEntry
->PatchInformation
);
404 /* Load new symbol information */
405 if (! RosSymCreateFromMem(LdrEntry
->DllBase
,
406 LdrEntry
->SizeOfImage
,
407 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
))
409 /* Error loading symbol info, try to load it from file */
410 KdbpSymLoadModuleSymbols(&LdrEntry
->FullDllName
,
411 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
);
413 /* It already added symbols to cache */
417 /* Add file to cache */
418 KdbpSymAddCachedFile(&LdrEntry
->FullDllName
, LdrEntry
->PatchInformation
);
421 DPRINT("Installed symbols: %wZ@%p-%p %p\n",
422 &LdrEntry
->BaseDllName
,
424 (PVOID
)(LdrEntry
->SizeOfImage
+ (ULONG_PTR
)LdrEntry
->DllBase
),
425 LdrEntry
->PatchInformation
);
439 /*! \brief Initializes the KDB symbols implementation.
441 * \param DispatchTable Pointer to the KD dispatch table
442 * \param BootPhase Phase of initialization
447 PKD_DISPATCH_TABLE DispatchTable
,
453 PLDR_DATA_TABLE_ENTRY LdrEntry
;
455 DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase
);
460 /* Load symbols only if we have 96Mb of RAM or more */
461 if (MmNumberOfPhysicalPages
>= 0x6000)
467 /* Write out the functions that we support for now */
468 DispatchTable
->KdpInitRoutine
= KdpKdbgInit
;
469 DispatchTable
->KdpPrintRoutine
= KdbDebugPrint
;
471 /* Register as a Provider */
472 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
474 /* Perform actual initialization of symbol module */
475 //NtoskrnlModuleObject->PatchInformation = NULL;
476 //LdrHalModuleObject->PatchInformation = NULL;
478 InitializeListHead(&SymbolFileListHead
);
479 KeInitializeSpinLock(&SymbolFileListLock
);
481 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
482 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
483 ASSERT(KeLoaderBlock
);
484 p1
= KeLoaderBlock
->LoadOptions
;
485 while('\0' != *p1
&& NULL
!= (p2
= strchr(p1
, '/')))
489 if (0 == _strnicmp(p2
, "LOADSYMBOLS", 11))
494 else if (0 == _strnicmp(p2
, "NOLOADSYMBOLS", 13))
512 YesNo
= toupper(*p2
);
513 if ('N' == YesNo
|| 'F' == YesNo
|| '0' == YesNo
)
518 LoadSymbols
= (0 < Found
);
523 RosSymInitKernelMode();
525 else if (BootPhase
== 1)
527 /* Load symbols for NTOSKRNL.EXE.
528 It is always the first module in PsLoadedModuleList. KeLoaderBlock can't be used here as its content is just temporary. */
529 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
530 KdbSymProcessSymbols(LdrEntry
);
532 /* Also load them for HAL.DLL. */
533 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
->Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
534 KdbSymProcessSymbols(LdrEntry
);
536 KdbpSymbolsInitialized
= TRUE
;