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