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