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 *****************************************************************/
16 #include "../cache/section/newmm.h"
19 /* GLOBALS ******************************************************************/
21 typedef struct _IMAGE_SYMBOL_INFO_CACHE
25 UNICODE_STRING FileName
;
26 PROSSYM_INFO RosSymInfo
;
28 IMAGE_SYMBOL_INFO_CACHE
, *PIMAGE_SYMBOL_INFO_CACHE
;
30 typedef struct _ROSSYM_KM_OWN_CONTEXT
{
31 LARGE_INTEGER FileOffset
;
32 PFILE_OBJECT FileObject
;
33 } ROSSYM_KM_OWN_CONTEXT
, *PROSSYM_KM_OWN_CONTEXT
;
35 static BOOLEAN LoadSymbols
;
36 static LIST_ENTRY SymbolFileListHead
;
37 static KSPIN_LOCK SymbolFileListLock
;
38 static PROSSYM_INFO KdbpRosSymInfo
;
39 static ULONG_PTR KdbpImageBase
;
40 BOOLEAN KdbpSymbolsInitialized
= FALSE
;
42 /* FUNCTIONS ****************************************************************/
45 KdbpSeekSymFile(PVOID FileContext
, ULONG_PTR Target
)
47 PROSSYM_KM_OWN_CONTEXT Context
= (PROSSYM_KM_OWN_CONTEXT
)FileContext
;
48 Context
->FileOffset
.QuadPart
= Target
;
53 KdbpReadSymFile(PVOID FileContext
, PVOID Buffer
, ULONG Length
)
55 PROSSYM_KM_OWN_CONTEXT Context
= (PROSSYM_KM_OWN_CONTEXT
)FileContext
;
57 NTSTATUS Status
= MiSimpleRead
64 return NT_SUCCESS(Status
);
67 static PROSSYM_KM_OWN_CONTEXT
68 KdbpCaptureFileForSymbols(PFILE_OBJECT FileObject
)
70 PROSSYM_KM_OWN_CONTEXT Context
= ExAllocatePool(NonPagedPool
, sizeof(*Context
));
71 if (!Context
) return NULL
;
72 ObReferenceObject(FileObject
);
73 Context
->FileOffset
.QuadPart
= 0;
74 Context
->FileObject
= FileObject
;
79 KdbpReleaseFileForSymbols(PROSSYM_KM_OWN_CONTEXT Context
)
81 ObDereferenceObject(Context
->FileObject
);
86 KdbpSymSearchModuleList(
87 IN PLIST_ENTRY current_entry
,
88 IN PLIST_ENTRY end_entry
,
93 OUT PLDR_DATA_TABLE_ENTRY
* pLdrEntry
)
95 while (current_entry
&& current_entry
!= end_entry
)
97 *pLdrEntry
= CONTAINING_RECORD(current_entry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
99 if ((Address
&& Address
>= (PVOID
)(*pLdrEntry
)->DllBase
&& Address
< (PVOID
)((ULONG_PTR
)(*pLdrEntry
)->DllBase
+ (*pLdrEntry
)->SizeOfImage
)) ||
100 (Name
&& !_wcsnicmp((*pLdrEntry
)->BaseDllName
.Buffer
, Name
, (*pLdrEntry
)->BaseDllName
.Length
/ sizeof(WCHAR
))) ||
101 (Index
>= 0 && (*Count
)++ == Index
))
106 current_entry
= current_entry
->Flink
;
112 /*! \brief Find a module...
114 * \param Address If \a Address is not NULL the module containing \a Address
116 * \param Name If \a Name is not NULL the module named \a Name will be
118 * \param Index If \a Index is >= 0 the Index'th module will be returned.
119 * \param pLdrEntry Pointer to a PLDR_DATA_TABLE_ENTRY which is filled.
121 * \retval TRUE Module was found, \a pLdrEntry was filled.
122 * \retval FALSE No module was found.
126 IN PVOID Address OPTIONAL
,
127 IN LPCWSTR Name OPTIONAL
,
128 IN INT Index OPTIONAL
,
129 OUT PLDR_DATA_TABLE_ENTRY
* pLdrEntry
)
132 PEPROCESS CurrentProcess
;
134 /* First try to look up the module in the kernel module list. */
135 if(KdbpSymSearchModuleList(PsLoadedModuleList
.Flink
,
146 /* That didn't succeed. Try the module list of the current process now. */
147 CurrentProcess
= PsGetCurrentProcess();
149 if(!CurrentProcess
|| !CurrentProcess
->Peb
|| !CurrentProcess
->Peb
->Ldr
)
152 return KdbpSymSearchModuleList(CurrentProcess
->Peb
->Ldr
->InLoadOrderModuleList
.Flink
,
153 &CurrentProcess
->Peb
->Ldr
->InLoadOrderModuleList
,
161 /*! \brief Print address...
163 * Tries to lookup line number, file name and function name for the given
164 * address and prints it.
165 * If no such information is found the address is printed in the format
166 * <module: offset>, otherwise the format will be
167 * <module: offset (filename:linenumber (functionname))>
169 * \retval TRUE Module containing \a Address was found, \a Address was printed.
170 * \retval FALSE No module containing \a Address was found, nothing was printed.
175 IN PKTRAP_FRAME Context
)
178 PMEMORY_AREA MemoryArea
= NULL
;
179 PROS_SECTION_OBJECT SectionObject
;
180 PLDR_DATA_TABLE_ENTRY LdrEntry
;
181 PROSSYM_KM_OWN_CONTEXT FileContext
;
182 ULONG_PTR RelativeAddress
;
184 ROSSYM_LINEINFO LineInfo
= {0};
187 enum _ROSSYM_REGNAME regname
;
190 { ROSSYM_X86_EDX
, FIELD_OFFSET(KTRAP_FRAME
, Edx
) },
191 { ROSSYM_X86_EAX
, FIELD_OFFSET(KTRAP_FRAME
, Eax
) },
192 { ROSSYM_X86_ECX
, FIELD_OFFSET(KTRAP_FRAME
, Ecx
) },
193 { ROSSYM_X86_EBX
, FIELD_OFFSET(KTRAP_FRAME
, Ebx
) },
194 { ROSSYM_X86_ESI
, FIELD_OFFSET(KTRAP_FRAME
, Esi
) },
195 { ROSSYM_X86_EDI
, FIELD_OFFSET(KTRAP_FRAME
, Edi
) },
196 { ROSSYM_X86_EBP
, FIELD_OFFSET(KTRAP_FRAME
, Ebp
) },
197 { ROSSYM_X86_ESP
, FIELD_OFFSET(KTRAP_FRAME
, HardwareEsp
) }
202 DPRINT("Has Context %x (EBP %x)\n", Context
, Context
->Ebp
);
203 LineInfo
.Flags
= ROSSYM_LINEINFO_HAS_REGISTERS
;
205 for (i
= 0; i
< sizeof(regmap
) / sizeof(regmap
[0]); i
++) {
207 (&LineInfo
.Registers
.Registers
[regmap
[i
].regname
],
208 ((PCHAR
)Context
)+regmap
[i
].ctx_offset
,
210 DPRINT("DWARF REG[%d] -> %x\n", regmap
[i
].regname
, LineInfo
.Registers
.Registers
[regmap
[i
].regname
]);
214 if (!KdbpSymbolsInitialized
|| !KdbpSymFindModule(Address
, NULL
, -1, &LdrEntry
))
217 RelativeAddress
= (ULONG_PTR
)Address
- (ULONG_PTR
)LdrEntry
->DllBase
;
218 Status
= KdbSymGetAddressInformation
219 (LdrEntry
->PatchInformation
,
223 if (NT_SUCCESS(Status
))
225 DbgPrint("<%wZ:%x (%s:%d (%s))>",
226 &LdrEntry
->BaseDllName
, RelativeAddress
, LineInfo
.FileName
, LineInfo
.LineNumber
, LineInfo
.FunctionName
);
232 for (i
= 0; i
< LineInfo
.NumParams
; i
++) {
236 LineInfo
.Parameters
[i
].ValueName
,
237 LineInfo
.Parameters
[i
].Value
);
245 else if (Address
< MmSystemRangeStart
)
247 MemoryArea
= MmLocateMemoryAreaByAddress(&PsGetCurrentProcess()->Vm
, Address
);
248 if (!MemoryArea
|| MemoryArea
->Type
!= MEMORY_AREA_SECTION_VIEW
)
252 SectionObject
= MemoryArea
->Data
.SectionData
.Section
;
253 if (!(SectionObject
->AllocationAttributes
& SEC_IMAGE
)) goto end
;
254 if (MemoryArea
->StartingAddress
!= (PVOID
)KdbpImageBase
)
258 RosSymDelete(KdbpRosSymInfo
);
259 KdbpRosSymInfo
= NULL
;
263 if ((FileContext
= KdbpCaptureFileForSymbols(SectionObject
->FileObject
)))
265 if (RosSymCreateFromFile(FileContext
, &KdbpRosSymInfo
))
266 KdbpImageBase
= (ULONG_PTR
)MemoryArea
->StartingAddress
;
268 KdbpReleaseFileForSymbols(FileContext
);
274 RelativeAddress
= (ULONG_PTR
)Address
- KdbpImageBase
;
275 RosSymFreeInfo(&LineInfo
);
276 Status
= KdbSymGetAddressInformation
280 if (NT_SUCCESS(Status
))
283 ("<%wZ:%x (%s:%d (%s))>",
284 &SectionObject
->FileObject
->FileName
,
288 LineInfo
.FunctionName
);
295 for (i
= 0; i
< LineInfo
.NumParams
; i
++) {
299 LineInfo
.Parameters
[i
].ValueName
,
300 LineInfo
.Parameters
[i
].Value
);
312 DbgPrint("<%wZ:%x>", &LdrEntry
->BaseDllName
, RelativeAddress
);
318 /*! \brief Get information for an address (source file, line number,
321 * \param SymbolInfo Pointer to ROSSYM_INFO.
322 * \param RelativeAddress Relative address to look up.
323 * \param LineNumber Pointer to an ULONG which is filled with the line
324 * number (can be NULL)
325 * \param FileName Pointer to an array of CHARs which gets filled with
326 * the filename (can be NULL)
327 * \param FunctionName Pointer to an array of CHARs which gets filled with
328 * the function name (can be NULL)
330 * \returns NTSTATUS error code.
331 * \retval STATUS_SUCCESS At least one of the requested informations was found.
332 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
335 KdbSymGetAddressInformation(
336 IN PROSSYM_INFO RosSymInfo
,
337 IN ULONG_PTR RelativeAddress
,
338 IN PROSSYM_LINEINFO LineInfo
)
340 if (!KdbpSymbolsInitialized
||
342 !RosSymGetAddressInformation(RosSymInfo
, RelativeAddress
, LineInfo
))
344 return STATUS_UNSUCCESSFUL
;
347 return STATUS_SUCCESS
;
350 /*! \brief Find cached symbol file.
352 * Looks through the list of cached symbol files and tries to find an already
355 * \param FileName FileName of the symbol file to look for.
357 * \returns A pointer to the cached symbol info.
358 * \retval NULL No cached info found.
360 * \sa KdbpSymAddCachedFile
363 KdbpSymFindCachedFile(
364 IN PUNICODE_STRING FileName
)
366 PIMAGE_SYMBOL_INFO_CACHE Current
;
367 PLIST_ENTRY CurrentEntry
;
370 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
372 CurrentEntry
= SymbolFileListHead
.Flink
;
373 while (CurrentEntry
!= (&SymbolFileListHead
))
375 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
377 if (RtlEqualUnicodeString(&Current
->FileName
, FileName
, TRUE
))
380 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
381 DPRINT("Found cached file!\n");
382 return Current
->RosSymInfo
;
385 CurrentEntry
= CurrentEntry
->Flink
;
388 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
390 DPRINT("Cached file not found!\n");
394 /*! \brief Add a symbol file to the cache.
396 * \param FileName Filename of the symbol file.
397 * \param RosSymInfo Pointer to the symbol info.
399 * \sa KdbpSymRemoveCachedFile
402 KdbpSymAddCachedFile(
403 IN PUNICODE_STRING FileName
,
404 IN PROSSYM_INFO RosSymInfo
)
406 PIMAGE_SYMBOL_INFO_CACHE CacheEntry
;
408 DPRINT("Adding symbol file: %wZ RosSymInfo = %p\n", FileName
, RosSymInfo
);
411 CacheEntry
= ExAllocatePoolWithTag(NonPagedPool
, sizeof (IMAGE_SYMBOL_INFO_CACHE
), TAG_KDBS
);
413 RtlZeroMemory(CacheEntry
, sizeof (IMAGE_SYMBOL_INFO_CACHE
));
416 CacheEntry
->FileName
.Buffer
= ExAllocatePoolWithTag(NonPagedPool
,
419 CacheEntry
->FileName
.MaximumLength
= FileName
->Length
;
420 RtlCopyUnicodeString(&CacheEntry
->FileName
, FileName
);
421 ASSERT(CacheEntry
->FileName
.Buffer
);
422 CacheEntry
->RefCount
= 1;
423 CacheEntry
->RosSymInfo
= RosSymInfo
;
424 InsertTailList(&SymbolFileListHead
, &CacheEntry
->ListEntry
); /* FIXME: Lock list? */
427 /*! \brief Remove a symbol file (reference) from the cache.
429 * Tries to find a cache entry matching the given symbol info and decreases
430 * it's reference count. If the refcount is 0 after decreasing it the cache
431 * entry will be removed from the list and freed.
433 * \param RosSymInfo Pointer to the symbol info.
435 * \sa KdbpSymAddCachedFile
438 KdbpSymRemoveCachedFile(
439 IN PROSSYM_INFO RosSymInfo
)
441 PIMAGE_SYMBOL_INFO_CACHE Current
;
442 PLIST_ENTRY CurrentEntry
;
445 KeAcquireSpinLock(&SymbolFileListLock
, &Irql
);
447 CurrentEntry
= SymbolFileListHead
.Flink
;
448 while (CurrentEntry
!= (&SymbolFileListHead
))
450 Current
= CONTAINING_RECORD(CurrentEntry
, IMAGE_SYMBOL_INFO_CACHE
, ListEntry
);
452 if (Current
->RosSymInfo
== RosSymInfo
) /* found */
454 ASSERT(Current
->RefCount
> 0);
456 if (Current
->RefCount
< 1)
458 RemoveEntryList(&Current
->ListEntry
);
459 RosSymDelete(Current
->RosSymInfo
);
463 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
467 CurrentEntry
= CurrentEntry
->Flink
;
470 KeReleaseSpinLock(&SymbolFileListLock
, Irql
);
473 /*! \brief Loads a symbol file.
475 * \param FileName Filename of the symbol file to load.
476 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled.
478 * \sa KdbpSymUnloadModuleSymbols
481 KdbpSymLoadModuleSymbols(
482 IN PUNICODE_STRING FileName
,
483 OUT PROSSYM_INFO
*RosSymInfo
)
485 OBJECT_ATTRIBUTES ObjectAttributes
;
488 IO_STATUS_BLOCK IoStatusBlock
;
489 PFILE_OBJECT FileObject
;
490 PROSSYM_KM_OWN_CONTEXT FileContext
;
492 /* Allow KDB to break on module load */
493 KdbModuleLoaded(FileName
);
501 /* Try to find cached (already loaded) symbol file */
502 *RosSymInfo
= KdbpSymFindCachedFile(FileName
);
505 DPRINT("Found cached symbol file %wZ\n", FileName
);
510 InitializeObjectAttributes(&ObjectAttributes
,
512 OBJ_CASE_INSENSITIVE
|OBJ_KERNEL_HANDLE
,
516 DPRINT("Attempting to open image: %wZ\n", FileName
);
518 Status
= ZwOpenFile(&FileHandle
,
522 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
523 FILE_NON_DIRECTORY_FILE
|FILE_SYNCHRONOUS_IO_NONALERT
);
524 if (!NT_SUCCESS(Status
))
526 DPRINT("Could not open image file(%x): %wZ\n", Status
, FileName
);
530 DPRINT("Loading symbols from %wZ...\n", FileName
);
532 Status
= ObReferenceObjectByHandle
534 FILE_READ_DATA
|SYNCHRONIZE
,
540 if (!NT_SUCCESS(Status
))
542 DPRINT("Could not get the file object\n");
547 if ((FileContext
= KdbpCaptureFileForSymbols(FileObject
)))
549 if (RosSymCreateFromFile(FileContext
, RosSymInfo
))
551 /* add file to cache */
553 UNICODE_STRING TruncatedName
= *FileName
;
554 for (i
= (TruncatedName
.Length
/ sizeof(WCHAR
)) - 1; i
>= 0; i
--)
555 if (TruncatedName
.Buffer
[i
] == '\\') {
556 TruncatedName
.Buffer
+= i
+1;
557 TruncatedName
.Length
-= (i
+1)*sizeof(WCHAR
);
558 TruncatedName
.MaximumLength
-= (i
+1)*sizeof(WCHAR
);
561 KdbpSymAddCachedFile(&TruncatedName
, *RosSymInfo
);
562 DPRINT("Installed symbols: %wZ %p\n", &TruncatedName
, *RosSymInfo
);
564 KdbpReleaseFileForSymbols(FileContext
);
567 ObDereferenceObject(FileObject
);
572 KdbSymProcessSymbols(
573 IN PLDR_DATA_TABLE_ENTRY LdrEntry
)
577 LdrEntry
->PatchInformation
= NULL
;
581 /* Remove symbol info if it already exists */
582 if (LdrEntry
->PatchInformation
) {
583 KdbpSymRemoveCachedFile(LdrEntry
->PatchInformation
);
586 /* Error loading symbol info, try to load it from file */
587 KdbpSymLoadModuleSymbols(&LdrEntry
->FullDllName
,
588 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
);
590 if (!LdrEntry
->PatchInformation
) {
591 // HACK: module dll names don't identify the real files
592 UNICODE_STRING SystemRoot
;
593 UNICODE_STRING ModuleNameCopy
;
594 RtlInitUnicodeString(&SystemRoot
, L
"\\SystemRoot\\system32\\Drivers\\");
595 ModuleNameCopy
.Length
= 0;
596 ModuleNameCopy
.MaximumLength
=
597 LdrEntry
->BaseDllName
.MaximumLength
+ SystemRoot
.MaximumLength
;
598 ModuleNameCopy
.Buffer
= ExAllocatePool(NonPagedPool
, SystemRoot
.MaximumLength
+ LdrEntry
->BaseDllName
.MaximumLength
);
599 RtlCopyUnicodeString(&ModuleNameCopy
, &SystemRoot
);
601 (ModuleNameCopy
.Buffer
+ ModuleNameCopy
.Length
/ sizeof(WCHAR
),
602 LdrEntry
->BaseDllName
.Buffer
,
603 LdrEntry
->BaseDllName
.Length
);
604 ModuleNameCopy
.Length
+= LdrEntry
->BaseDllName
.Length
;
605 KdbpSymLoadModuleSymbols(&ModuleNameCopy
,
606 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
);
607 if (!LdrEntry
->PatchInformation
) {
608 SystemRoot
.Length
-= strlen("Drivers\\") * sizeof(WCHAR
);
609 RtlCopyUnicodeString(&ModuleNameCopy
, &SystemRoot
);
611 (ModuleNameCopy
.Buffer
+ ModuleNameCopy
.Length
/ sizeof(WCHAR
),
612 LdrEntry
->BaseDllName
.Buffer
,
613 LdrEntry
->BaseDllName
.Length
);
614 ModuleNameCopy
.Length
+= LdrEntry
->BaseDllName
.Length
;
615 KdbpSymLoadModuleSymbols(&ModuleNameCopy
,
616 (PROSSYM_INFO
*)&LdrEntry
->PatchInformation
);
618 RtlFreeUnicodeString(&ModuleNameCopy
);
621 /* It already added symbols to cache */
622 DPRINT("Installed symbols: %wZ@%p-%p %p\n",
623 &LdrEntry
->BaseDllName
,
625 (PVOID
)(LdrEntry
->SizeOfImage
+ (ULONG_PTR
)LdrEntry
->DllBase
),
626 LdrEntry
->PatchInformation
);
638 static PVOID
KdbpSymAllocMem(ULONG_PTR size
)
640 return ExAllocatePoolWithTag(NonPagedPool
, size
, 'RSYM');
643 static VOID
KdbpSymFreeMem(PVOID Area
)
645 return ExFreePool(Area
);
648 static BOOLEAN
KdbpSymReadMem(PVOID FileContext
, PVOID TargetDebug
, PVOID SourceMem
, ULONG Size
)
650 return NT_SUCCESS(KdbpSafeReadMemory(TargetDebug
, SourceMem
, Size
));
653 static ROSSYM_CALLBACKS KdbpRosSymCallbacks
= {
654 KdbpSymAllocMem
, KdbpSymFreeMem
,
655 KdbpReadSymFile
, KdbpSeekSymFile
,
659 /*! \brief Initializes the KDB symbols implementation.
661 * \param DispatchTable Pointer to the KD dispatch table
662 * \param BootPhase Phase of initialization
667 PKD_DISPATCH_TABLE DispatchTable
,
673 PLDR_DATA_TABLE_ENTRY LdrEntry
;
675 DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase
);
680 /* Load symbols only if we have 96Mb of RAM or more */
681 if (MmNumberOfPhysicalPages
>= 0x6000)
687 /* Write out the functions that we support for now */
688 DispatchTable
->KdpInitRoutine
= KdpKdbgInit
;
689 DispatchTable
->KdpPrintRoutine
= KdbDebugPrint
;
691 /* Register as a Provider */
692 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
694 /* Perform actual initialization of symbol module */
695 //NtoskrnlModuleObject->PatchInformation = NULL;
696 //LdrHalModuleObject->PatchInformation = NULL;
698 InitializeListHead(&SymbolFileListHead
);
699 KeInitializeSpinLock(&SymbolFileListLock
);
701 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
702 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
703 ASSERT(KeLoaderBlock
);
704 p1
= KeLoaderBlock
->LoadOptions
;
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 RosSymInit(&KdbpRosSymCallbacks
);
745 else if (BootPhase
== 3)
747 /* Load symbols for NTOSKRNL.EXE.
748 It is always the first module in PsLoadedModuleList. KeLoaderBlock can't be used here as its content is just temporary. */
749 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
750 KdbSymProcessSymbols(LdrEntry
);
752 /* Also load them for HAL.DLL. */
753 LdrEntry
= CONTAINING_RECORD(PsLoadedModuleList
.Flink
->Flink
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
754 KdbSymProcessSymbols(LdrEntry
);
756 KdbpSymbolsInitialized
= TRUE
;