xref: /reactos/ntoskrnl/kdbg/kdb_symbols.c (revision bbabe248)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/kdbg/kdb_symbols.c
5  * PURPOSE:         Getting symbol information...
6  *
7  * PROGRAMMERS:     David Welch (welch@cwcom.net)
8  *                  Colin Finck (colin@reactos.org)
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <ntoskrnl.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 /* GLOBALS ******************************************************************/
19 
20 typedef struct _IMAGE_SYMBOL_INFO_CACHE
21 {
22     LIST_ENTRY ListEntry;
23     ULONG RefCount;
24     UNICODE_STRING FileName;
25     PROSSYM_INFO RosSymInfo;
26 }
27 IMAGE_SYMBOL_INFO_CACHE, *PIMAGE_SYMBOL_INFO_CACHE;
28 
29 static BOOLEAN LoadSymbols;
30 static LIST_ENTRY SymbolsToLoad;
31 static KSPIN_LOCK SymbolsToLoadLock;
32 static KEVENT SymbolsToLoadEvent;
33 
34 /* FUNCTIONS ****************************************************************/
35 
36 static
37 BOOLEAN
38 KdbpSymSearchModuleList(
39     IN PLIST_ENTRY current_entry,
40     IN PLIST_ENTRY end_entry,
41     IN PLONG Count,
42     IN PVOID Address,
43     IN INT Index,
44     OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry)
45 {
46     while (current_entry && current_entry != end_entry)
47     {
48         *pLdrEntry = CONTAINING_RECORD(current_entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
49 
50         if ((Address && Address >= (PVOID)(*pLdrEntry)->DllBase && Address < (PVOID)((ULONG_PTR)(*pLdrEntry)->DllBase + (*pLdrEntry)->SizeOfImage)) ||
51             (Index >= 0 && (*Count)++ == Index))
52         {
53             return TRUE;
54         }
55 
56         current_entry = current_entry->Flink;
57     }
58 
59     return FALSE;
60 }
61 
62 /*! \brief Find a module...
63  *
64  * \param Address      If \a Address is not NULL the module containing \a Address
65  *                     is searched.
66  * \param Name         If \a Name is not NULL the module named \a Name will be
67  *                     searched.
68  * \param Index        If \a Index is >= 0 the Index'th module will be returned.
69  * \param pLdrEntry    Pointer to a PLDR_DATA_TABLE_ENTRY which is filled.
70  *
71  * \retval TRUE    Module was found, \a pLdrEntry was filled.
72  * \retval FALSE   No module was found.
73  */
74 BOOLEAN
75 KdbpSymFindModule(
76     IN PVOID Address  OPTIONAL,
77     IN INT Index  OPTIONAL,
78     OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry)
79 {
80     LONG Count = 0;
81     PEPROCESS CurrentProcess;
82 
83     /* First try to look up the module in the kernel module list. */
84     KeAcquireSpinLockAtDpcLevel(&PsLoadedModuleSpinLock);
85     if(KdbpSymSearchModuleList(PsLoadedModuleList.Flink,
86                                &PsLoadedModuleList,
87                                &Count,
88                                Address,
89                                Index,
90                                pLdrEntry))
91     {
92         KeReleaseSpinLockFromDpcLevel(&PsLoadedModuleSpinLock);
93         return TRUE;
94     }
95     KeReleaseSpinLockFromDpcLevel(&PsLoadedModuleSpinLock);
96 
97     /* That didn't succeed. Try the module list of the current process now. */
98     CurrentProcess = PsGetCurrentProcess();
99 
100     if(!CurrentProcess || !CurrentProcess->Peb || !CurrentProcess->Peb->Ldr)
101         return FALSE;
102 
103     return KdbpSymSearchModuleList(CurrentProcess->Peb->Ldr->InLoadOrderModuleList.Flink,
104                                    &CurrentProcess->Peb->Ldr->InLoadOrderModuleList,
105                                    &Count,
106                                    Address,
107                                    Index,
108                                    pLdrEntry);
109 }
110 
111 static
112 PCHAR
113 NTAPI
114 KdbpSymUnicodeToAnsi(IN PUNICODE_STRING Unicode,
115                      OUT PCHAR Ansi,
116                      IN ULONG Length)
117 {
118     PCHAR p;
119     PWCHAR pw;
120     ULONG i;
121 
122     /* Set length and normalize it */
123     i = Unicode->Length / sizeof(WCHAR);
124     i = min(i, Length - 1);
125 
126     /* Set source and destination, and copy */
127     pw = Unicode->Buffer;
128     p = Ansi;
129     while (i--) *p++ = (CHAR)*pw++;
130 
131     /* Null terminate and return */
132     *p = ANSI_NULL;
133     return Ansi;
134 }
135 
136 /*! \brief Print address...
137  *
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))>
143  *
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.
146  */
147 BOOLEAN
148 KdbSymPrintAddress(
149     IN PVOID Address,
150     IN PCONTEXT Context)
151 {
152     PLDR_DATA_TABLE_ENTRY LdrEntry;
153     ULONG_PTR RelativeAddress;
154     BOOLEAN Printed = FALSE;
155     CHAR ModuleNameAnsi[64];
156 
157     if (!KdbpSymFindModule(Address, -1, &LdrEntry))
158         return FALSE;
159 
160     RelativeAddress = (ULONG_PTR)Address - (ULONG_PTR)LdrEntry->DllBase;
161 
162     KdbpSymUnicodeToAnsi(&LdrEntry->BaseDllName,
163                         ModuleNameAnsi,
164                         sizeof(ModuleNameAnsi));
165 
166     if (LdrEntry->PatchInformation)
167     {
168         ULONG LineNumber;
169         CHAR FileName[256];
170         CHAR FunctionName[256];
171 
172         if (RosSymGetAddressInformation(LdrEntry->PatchInformation, RelativeAddress, &LineNumber, FileName, FunctionName))
173         {
174             STRING str;
175             /* Use KdpPrintString because KdpDprintf is limited wrt string size */
176             KdpDprintf("<%s:%x (", ModuleNameAnsi, RelativeAddress);
177             str.Buffer = FileName;
178             str.Length = strnlen(FileName, sizeof(FileName));
179             str.MaximumLength = sizeof(FileName);
180             KdpPrintString(&str);
181             KdpDprintf(":%d (%s))>", LineNumber, FunctionName);
182 
183             Printed = TRUE;
184         }
185     }
186 
187     if (!Printed)
188     {
189         /* Just print module & address */
190         KdpDprintf("<%s:%x>", ModuleNameAnsi, RelativeAddress);
191     }
192 
193     return TRUE;
194 }
195 
196 static KSTART_ROUTINE LoadSymbolsRoutine;
197 /*! \brief          The symbol loader thread routine.
198  *                  This opens the image file for reading and loads the symbols
199  *                  section from there.
200  *
201  * \note            We must do this because KdbSymProcessSymbols is
202  *                  called at high IRQL and we can't set the event from here
203  *
204  * \param Context   Unused
205  */
206 _Use_decl_annotations_
207 VOID
208 NTAPI
209 LoadSymbolsRoutine(
210     _In_ PVOID Context)
211 {
212     UNREFERENCED_PARAMETER(Context);
213 
214     while (TRUE)
215     {
216         PLIST_ENTRY ListEntry;
217         NTSTATUS Status = KeWaitForSingleObject(&SymbolsToLoadEvent, WrKernel, KernelMode, FALSE, NULL);
218         if (!NT_SUCCESS(Status))
219         {
220             DPRINT1("KeWaitForSingleObject failed?! 0x%08x\n", Status);
221             LoadSymbols = FALSE;
222             return;
223         }
224 
225         while ((ListEntry = ExInterlockedRemoveHeadList(&SymbolsToLoad, &SymbolsToLoadLock)))
226         {
227             PLDR_DATA_TABLE_ENTRY LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
228             HANDLE FileHandle;
229             OBJECT_ATTRIBUTES Attrib;
230             IO_STATUS_BLOCK Iosb;
231             InitializeObjectAttributes(&Attrib, &LdrEntry->FullDllName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
232             DPRINT1("Trying %wZ\n", &LdrEntry->FullDllName);
233             Status = ZwOpenFile(&FileHandle,
234                                 FILE_READ_ACCESS | SYNCHRONIZE,
235                                 &Attrib,
236                                 &Iosb,
237                                 FILE_SHARE_READ,
238                                 FILE_SYNCHRONOUS_IO_NONALERT);
239             if (!NT_SUCCESS(Status))
240             {
241                 /* Try system paths */
242                 static const UNICODE_STRING System32Dir = RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\");
243                 UNICODE_STRING ImagePath;
244                 WCHAR ImagePathBuffer[256];
245                 RtlInitEmptyUnicodeString(&ImagePath, ImagePathBuffer, sizeof(ImagePathBuffer));
246                 RtlCopyUnicodeString(&ImagePath, &System32Dir);
247                 RtlAppendUnicodeStringToString(&ImagePath, &LdrEntry->BaseDllName);
248                 InitializeObjectAttributes(&Attrib, &ImagePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
249                 DPRINT1("Trying %wZ\n", &ImagePath);
250                 Status = ZwOpenFile(&FileHandle,
251                                     FILE_READ_ACCESS | SYNCHRONIZE,
252                                     &Attrib,
253                                     &Iosb,
254                                     FILE_SHARE_READ,
255                                     FILE_SYNCHRONOUS_IO_NONALERT);
256                 if (!NT_SUCCESS(Status))
257                 {
258                     static const UNICODE_STRING DriversDir= RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\drivers\\");
259 
260                     RtlInitEmptyUnicodeString(&ImagePath, ImagePathBuffer, sizeof(ImagePathBuffer));
261                     RtlCopyUnicodeString(&ImagePath, &DriversDir);
262                     RtlAppendUnicodeStringToString(&ImagePath, &LdrEntry->BaseDllName);
263                     InitializeObjectAttributes(&Attrib, &ImagePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
264                     DPRINT1("Trying %wZ\n", &ImagePath);
265                     Status = ZwOpenFile(&FileHandle,
266                                         FILE_READ_ACCESS | SYNCHRONIZE,
267                                         &Attrib,
268                                         &Iosb,
269                                         FILE_SHARE_READ,
270                                         FILE_SYNCHRONOUS_IO_NONALERT);
271                 }
272             }
273 
274             if (!NT_SUCCESS(Status))
275             {
276                 DPRINT1("Failed opening file %wZ (%wZ) for reading symbols (0x%08x)\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName, Status);
277                 /* We took a ref previously */
278                 MmUnloadSystemImage(LdrEntry);
279                 continue;
280             }
281 
282             /* Hand it to Rossym */
283             if (!RosSymCreateFromFile(&FileHandle, (PROSSYM_INFO*)&LdrEntry->PatchInformation))
284                 LdrEntry->PatchInformation = NULL;
285 
286             /* We're done for this one. */
287             NtClose(FileHandle);
288             MmUnloadSystemImage(LdrEntry);
289         }
290     }
291 }
292 
293 /*! \brief          Load symbols from image mapping. If this fails,
294  *
295  * \param LdrEntry  The entry to load symbols from
296  */
297 VOID
298 KdbSymProcessSymbols(
299     _Inout_ PLDR_DATA_TABLE_ENTRY LdrEntry,
300     _In_ BOOLEAN Load)
301 {
302     if (!LoadSymbols)
303         return;
304 
305     /* Check if this is unload */
306     if (!Load)
307     {
308         /* Did we process it */
309         if (LdrEntry->PatchInformation)
310         {
311             RosSymDelete(LdrEntry->PatchInformation);
312             LdrEntry->PatchInformation = NULL;
313         }
314         return;
315     }
316 
317     if (RosSymCreateFromMem(LdrEntry->DllBase, LdrEntry->SizeOfImage, (PROSSYM_INFO*)&LdrEntry->PatchInformation))
318     {
319         return;
320     }
321 
322     /* Add a ref until we really process it */
323     LdrEntry->LoadCount++;
324 
325     /* Tell our worker thread to read from it */
326     KeAcquireSpinLockAtDpcLevel(&SymbolsToLoadLock);
327     InsertTailList(&SymbolsToLoad, &LdrEntry->InInitializationOrderLinks);
328     KeReleaseSpinLockFromDpcLevel(&SymbolsToLoadLock);
329 
330     KeSetEvent(&SymbolsToLoadEvent, IO_NO_INCREMENT, FALSE);
331 }
332 
333 VOID
334 NTAPI
335 KdbDebugPrint(
336     PCH Message,
337     ULONG Length)
338 {
339     /* Nothing here */
340 }
341 
342 
343 /*! \brief Initializes the KDB symbols implementation.
344  *
345  * \param DispatchTable         Pointer to the KD dispatch table
346  * \param BootPhase             Phase of initialization
347  */
348 VOID
349 NTAPI
350 KdbInitialize(
351     PKD_DISPATCH_TABLE DispatchTable,
352     ULONG BootPhase)
353 {
354     PCHAR p1, p2;
355     SHORT Found = FALSE;
356     CHAR YesNo;
357 
358     DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase);
359 
360     LoadSymbols = FALSE;
361 
362 #if DBG
363     /* Load symbols only if we have 96Mb of RAM or more */
364     if (MmNumberOfPhysicalPages >= 0x6000)
365         LoadSymbols = TRUE;
366 #endif
367 
368     if (BootPhase == 0)
369     {
370         /* Write out the functions that we support for now */
371         DispatchTable->KdpInitRoutine = KdpKdbgInit;
372         DispatchTable->KdpPrintRoutine = KdbDebugPrint;
373 
374         /* Register as a Provider */
375         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
376 
377         /* Perform actual initialization of symbol module */
378         //NtoskrnlModuleObject->PatchInformation = NULL;
379         //LdrHalModuleObject->PatchInformation = NULL;
380 
381         /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS,
382         * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */
383         ASSERT(KeLoaderBlock);
384         p1 = KeLoaderBlock->LoadOptions;
385         while('\0' != *p1 && NULL != (p2 = strchr(p1, '/')))
386         {
387             p2++;
388             Found = 0;
389             if (0 == _strnicmp(p2, "LOADSYMBOLS", 11))
390             {
391                 Found = +1;
392                 p2 += 11;
393             }
394             else if (0 == _strnicmp(p2, "NOLOADSYMBOLS", 13))
395             {
396                 Found = -1;
397                 p2 += 13;
398             }
399             if (0 != Found)
400             {
401                 while (isspace(*p2))
402                 {
403                     p2++;
404                 }
405                 if ('=' == *p2)
406                 {
407                     p2++;
408                     while (isspace(*p2))
409                     {
410                         p2++;
411                     }
412                     YesNo = toupper(*p2);
413                     if ('N' == YesNo || 'F' == YesNo || '0' == YesNo)
414                     {
415                         Found = -1 * Found;
416                     }
417                 }
418                 LoadSymbols = (0 < Found);
419             }
420             p1 = p2;
421         }
422     }
423     else if ((BootPhase == 1) && LoadSymbols)
424     {
425         HANDLE Thread;
426         NTSTATUS Status;
427         KIRQL OldIrql;
428 
429         /* Launch our worker thread */
430         InitializeListHead(&SymbolsToLoad);
431         KeInitializeSpinLock(&SymbolsToLoadLock);
432         KeInitializeEvent(&SymbolsToLoadEvent, SynchronizationEvent, FALSE);
433 
434         Status = PsCreateSystemThread(&Thread, THREAD_ALL_ACCESS, NULL, NULL, NULL, LoadSymbolsRoutine, NULL);
435         if (!NT_SUCCESS(Status))
436         {
437             DPRINT1("Failed starting symbols loader thread: 0x%08x\n", Status);
438             LoadSymbols = FALSE;
439             return;
440         }
441 
442         RosSymInitKernelMode();
443 
444         KeAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
445 
446         PLIST_ENTRY ListEntry = PsLoadedModuleList.Flink;
447         while (ListEntry != &PsLoadedModuleList)
448         {
449             PLDR_DATA_TABLE_ENTRY LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
450             KdbSymProcessSymbols(LdrEntry, TRUE);
451             ListEntry = ListEntry->Flink;
452         }
453 
454         KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
455     }
456 }
457 
458 /* EOF */
459