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 SymbolFileListHead; 31 static KSPIN_LOCK SymbolFileListLock; 32 BOOLEAN KdbpSymbolsInitialized = FALSE; 33 34 /* FUNCTIONS ****************************************************************/ 35 36 static NTSTATUS 37 KdbSymGetAddressInformation( 38 IN PROSSYM_INFO RosSymInfo, 39 IN ULONG_PTR RelativeAddress, 40 OUT PULONG LineNumber OPTIONAL, 41 OUT PCH FileName OPTIONAL, 42 OUT PCH FunctionName OPTIONAL); 43 44 static BOOLEAN 45 KdbpSymSearchModuleList( 46 IN PLIST_ENTRY current_entry, 47 IN PLIST_ENTRY end_entry, 48 IN PLONG Count, 49 IN PVOID Address, 50 IN LPCWSTR Name, 51 IN INT Index, 52 OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry) 53 { 54 while (current_entry && current_entry != end_entry) 55 { 56 *pLdrEntry = CONTAINING_RECORD(current_entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); 57 58 if ((Address && Address >= (PVOID)(*pLdrEntry)->DllBase && Address < (PVOID)((ULONG_PTR)(*pLdrEntry)->DllBase + (*pLdrEntry)->SizeOfImage)) || 59 (Name && !_wcsnicmp((*pLdrEntry)->BaseDllName.Buffer, Name, (*pLdrEntry)->BaseDllName.Length / sizeof(WCHAR))) || 60 (Index >= 0 && (*Count)++ == Index)) 61 { 62 return TRUE; 63 } 64 65 current_entry = current_entry->Flink; 66 } 67 68 return FALSE; 69 } 70 71 /*! \brief Find a module... 72 * 73 * \param Address If \a Address is not NULL the module containing \a Address 74 * is searched. 75 * \param Name If \a Name is not NULL the module named \a Name will be 76 * searched. 77 * \param Index If \a Index is >= 0 the Index'th module will be returned. 78 * \param pLdrEntry Pointer to a PLDR_DATA_TABLE_ENTRY which is filled. 79 * 80 * \retval TRUE Module was found, \a pLdrEntry was filled. 81 * \retval FALSE No module was found. 82 */ 83 BOOLEAN 84 KdbpSymFindModule( 85 IN PVOID Address OPTIONAL, 86 IN LPCWSTR Name OPTIONAL, 87 IN INT Index OPTIONAL, 88 OUT PLDR_DATA_TABLE_ENTRY* pLdrEntry) 89 { 90 LONG Count = 0; 91 PEPROCESS CurrentProcess; 92 93 /* First try to look up the module in the kernel module list. */ 94 if(KdbpSymSearchModuleList(PsLoadedModuleList.Flink, 95 &PsLoadedModuleList, 96 &Count, 97 Address, 98 Name, 99 Index, 100 pLdrEntry)) 101 { 102 return TRUE; 103 } 104 105 /* That didn't succeed. Try the module list of the current process now. */ 106 CurrentProcess = PsGetCurrentProcess(); 107 108 if(!CurrentProcess || !CurrentProcess->Peb || !CurrentProcess->Peb->Ldr) 109 return FALSE; 110 111 return KdbpSymSearchModuleList(CurrentProcess->Peb->Ldr->InLoadOrderModuleList.Flink, 112 &CurrentProcess->Peb->Ldr->InLoadOrderModuleList, 113 &Count, 114 Address, 115 Name, 116 Index, 117 pLdrEntry); 118 } 119 120 PCHAR 121 NTAPI 122 KdbpSymUnicodeToAnsi(IN PUNICODE_STRING Unicode, 123 OUT PCHAR Ansi, 124 IN ULONG Length) 125 { 126 PCHAR p; 127 PWCHAR pw; 128 ULONG i; 129 130 /* Set length and normalize it */ 131 i = Unicode->Length / sizeof(WCHAR); 132 i = min(i, Length - 1); 133 134 /* Set source and destination, and copy */ 135 pw = Unicode->Buffer; 136 p = Ansi; 137 while (i--) *p++ = (CHAR)*pw++; 138 139 /* Null terminate and return */ 140 *p = ANSI_NULL; 141 return Ansi; 142 } 143 144 /*! \brief Print address... 145 * 146 * Tries to lookup line number, file name and function name for the given 147 * address and prints it. 148 * If no such information is found the address is printed in the format 149 * <module: offset>, otherwise the format will be 150 * <module: offset (filename:linenumber (functionname))> 151 * 152 * \retval TRUE Module containing \a Address was found, \a Address was printed. 153 * \retval FALSE No module containing \a Address was found, nothing was printed. 154 */ 155 BOOLEAN 156 KdbSymPrintAddress( 157 IN PVOID Address, 158 IN PCONTEXT Context) 159 { 160 PLDR_DATA_TABLE_ENTRY LdrEntry; 161 ULONG_PTR RelativeAddress; 162 NTSTATUS Status; 163 ULONG LineNumber; 164 CHAR FileName[256]; 165 CHAR FunctionName[256]; 166 CHAR ModuleNameAnsi[64]; 167 168 if (!KdbpSymbolsInitialized || !KdbpSymFindModule(Address, NULL, -1, &LdrEntry)) 169 return FALSE; 170 171 KdbpSymUnicodeToAnsi(&LdrEntry->BaseDllName, 172 ModuleNameAnsi, 173 sizeof(ModuleNameAnsi)); 174 175 RelativeAddress = (ULONG_PTR)Address - (ULONG_PTR)LdrEntry->DllBase; 176 Status = KdbSymGetAddressInformation(LdrEntry->PatchInformation, 177 RelativeAddress, 178 &LineNumber, 179 FileName, 180 FunctionName); 181 if (NT_SUCCESS(Status)) 182 { 183 DbgPrint("<%s:%x (%s:%d (%s))>", 184 ModuleNameAnsi, RelativeAddress, FileName, LineNumber, FunctionName); 185 } 186 else 187 { 188 DbgPrint("<%s:%x>", ModuleNameAnsi, RelativeAddress); 189 } 190 191 return TRUE; 192 } 193 194 195 /*! \brief Get information for an address (source file, line number, 196 * function name) 197 * 198 * \param SymbolInfo Pointer to ROSSYM_INFO. 199 * \param RelativeAddress Relative address to look up. 200 * \param LineNumber Pointer to an ULONG which is filled with the line 201 * number (can be NULL) 202 * \param FileName Pointer to an array of CHARs which gets filled with 203 * the filename (can be NULL) 204 * \param FunctionName Pointer to an array of CHARs which gets filled with 205 * the function name (can be NULL) 206 * 207 * \returns NTSTATUS error code. 208 * \retval STATUS_SUCCESS At least one of the requested informations was found. 209 * \retval STATUS_UNSUCCESSFUL None of the requested information was found. 210 */ 211 static NTSTATUS 212 KdbSymGetAddressInformation( 213 IN PROSSYM_INFO RosSymInfo, 214 IN ULONG_PTR RelativeAddress, 215 OUT PULONG LineNumber OPTIONAL, 216 OUT PCH FileName OPTIONAL, 217 OUT PCH FunctionName OPTIONAL) 218 { 219 if (!KdbpSymbolsInitialized || 220 !RosSymInfo || 221 !RosSymGetAddressInformation(RosSymInfo, RelativeAddress, LineNumber, FileName, FunctionName)) 222 { 223 return STATUS_UNSUCCESSFUL; 224 } 225 226 return STATUS_SUCCESS; 227 } 228 229 /*! \brief Find cached symbol file. 230 * 231 * Looks through the list of cached symbol files and tries to find an already 232 * loaded one. 233 * 234 * \param FileName FileName of the symbol file to look for. 235 * 236 * \returns A pointer to the cached symbol info. 237 * \retval NULL No cached info found. 238 * 239 * \sa KdbpSymAddCachedFile 240 */ 241 static PROSSYM_INFO 242 KdbpSymFindCachedFile( 243 IN PUNICODE_STRING FileName) 244 { 245 PIMAGE_SYMBOL_INFO_CACHE Current; 246 PLIST_ENTRY CurrentEntry; 247 KIRQL Irql; 248 249 DPRINT("Looking for cached symbol file %wZ\n", FileName); 250 251 KeAcquireSpinLock(&SymbolFileListLock, &Irql); 252 253 CurrentEntry = SymbolFileListHead.Flink; 254 while (CurrentEntry != (&SymbolFileListHead)) 255 { 256 Current = CONTAINING_RECORD(CurrentEntry, IMAGE_SYMBOL_INFO_CACHE, ListEntry); 257 258 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current->FileName, FileName); 259 if (RtlEqualUnicodeString(&Current->FileName, FileName, TRUE)) 260 { 261 Current->RefCount++; 262 KeReleaseSpinLock(&SymbolFileListLock, Irql); 263 DPRINT("Found cached file!\n"); 264 return Current->RosSymInfo; 265 } 266 267 CurrentEntry = CurrentEntry->Flink; 268 } 269 270 KeReleaseSpinLock(&SymbolFileListLock, Irql); 271 272 DPRINT("Cached file not found!\n"); 273 return NULL; 274 } 275 276 /*! \brief Add a symbol file to the cache. 277 * 278 * \param FileName Filename of the symbol file. 279 * \param RosSymInfo Pointer to the symbol info. 280 * 281 * \sa KdbpSymRemoveCachedFile 282 */ 283 static VOID 284 KdbpSymAddCachedFile( 285 IN PUNICODE_STRING FileName, 286 IN PROSSYM_INFO RosSymInfo) 287 { 288 PIMAGE_SYMBOL_INFO_CACHE CacheEntry; 289 KIRQL Irql; 290 291 DPRINT("Adding symbol file: RosSymInfo = %p\n", RosSymInfo); 292 293 /* allocate entry */ 294 CacheEntry = ExAllocatePoolWithTag(NonPagedPool, sizeof (IMAGE_SYMBOL_INFO_CACHE), TAG_KDBS); 295 ASSERT(CacheEntry); 296 RtlZeroMemory(CacheEntry, sizeof (IMAGE_SYMBOL_INFO_CACHE)); 297 298 /* fill entry */ 299 CacheEntry->FileName.Buffer = ExAllocatePoolWithTag(NonPagedPool, 300 FileName->Length, 301 TAG_KDBS); 302 RtlCopyUnicodeString(&CacheEntry->FileName, FileName); 303 ASSERT(CacheEntry->FileName.Buffer); 304 CacheEntry->RefCount = 1; 305 CacheEntry->RosSymInfo = RosSymInfo; 306 KeAcquireSpinLock(&SymbolFileListLock, &Irql); 307 InsertTailList(&SymbolFileListHead, &CacheEntry->ListEntry); 308 KeReleaseSpinLock(&SymbolFileListLock, Irql); 309 } 310 311 /*! \brief Remove a symbol file (reference) from the cache. 312 * 313 * Tries to find a cache entry matching the given symbol info and decreases 314 * it's reference count. If the refcount is 0 after decreasing it the cache 315 * entry will be removed from the list and freed. 316 * 317 * \param RosSymInfo Pointer to the symbol info. 318 * 319 * \sa KdbpSymAddCachedFile 320 */ 321 static VOID 322 KdbpSymRemoveCachedFile( 323 IN PROSSYM_INFO RosSymInfo) 324 { 325 PIMAGE_SYMBOL_INFO_CACHE Current; 326 PLIST_ENTRY CurrentEntry; 327 KIRQL Irql; 328 329 KeAcquireSpinLock(&SymbolFileListLock, &Irql); 330 331 CurrentEntry = SymbolFileListHead.Flink; 332 while (CurrentEntry != (&SymbolFileListHead)) 333 { 334 Current = CONTAINING_RECORD(CurrentEntry, IMAGE_SYMBOL_INFO_CACHE, ListEntry); 335 336 if (Current->RosSymInfo == RosSymInfo) /* found */ 337 { 338 ASSERT(Current->RefCount > 0); 339 Current->RefCount--; 340 if (Current->RefCount < 1) 341 { 342 RemoveEntryList(&Current->ListEntry); 343 RosSymDelete(Current->RosSymInfo); 344 ExFreePool(Current); 345 } 346 347 KeReleaseSpinLock(&SymbolFileListLock, Irql); 348 return; 349 } 350 351 CurrentEntry = CurrentEntry->Flink; 352 } 353 354 KeReleaseSpinLock(&SymbolFileListLock, Irql); 355 DPRINT1("Warning: Removing unknown symbol file: RosSymInfo = %p\n", RosSymInfo); 356 } 357 358 /*! \brief Loads a symbol file. 359 * 360 * \param FileName Filename of the symbol file to load. 361 * \param RosSymInfo Pointer to a ROSSYM_INFO which gets filled. 362 * 363 * \sa KdbpSymUnloadModuleSymbols 364 */ 365 static VOID 366 KdbpSymLoadModuleSymbols( 367 IN PUNICODE_STRING FileName, 368 OUT PROSSYM_INFO *RosSymInfo) 369 { 370 OBJECT_ATTRIBUTES ObjectAttributes; 371 HANDLE FileHandle; 372 NTSTATUS Status; 373 IO_STATUS_BLOCK IoStatusBlock; 374 BOOLEAN Result; 375 376 /* Allow KDB to break on module load */ 377 KdbModuleLoaded(FileName); 378 379 if (!LoadSymbols) 380 { 381 *RosSymInfo = NULL; 382 return; 383 } 384 385 /* Try to find cached (already loaded) symbol file */ 386 *RosSymInfo = KdbpSymFindCachedFile(FileName); 387 if (*RosSymInfo) 388 { 389 DPRINT("Found cached symbol file %wZ\n", FileName); 390 return; 391 } 392 393 /* Open the file */ 394 InitializeObjectAttributes(&ObjectAttributes, 395 FileName, 396 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 397 NULL, 398 NULL); 399 400 DPRINT("Attempting to open image: %wZ\n", FileName); 401 402 Status = ZwOpenFile(&FileHandle, 403 FILE_READ_ACCESS | SYNCHRONIZE, 404 &ObjectAttributes, 405 &IoStatusBlock, 406 FILE_SHARE_READ | FILE_SHARE_WRITE, 407 FILE_SYNCHRONOUS_IO_NONALERT); 408 if (!NT_SUCCESS(Status)) 409 { 410 DPRINT("Could not open image file: %wZ\n", FileName); 411 return; 412 } 413 414 DPRINT("Loading symbols from %wZ...\n", FileName); 415 416 Result = RosSymCreateFromFile(&FileHandle, RosSymInfo); 417 ZwClose(FileHandle); 418 419 if (!Result) 420 { 421 DPRINT("Failed to load symbols from %wZ\n", FileName); 422 return; 423 } 424 425 DPRINT("Symbols loaded.\n"); 426 427 /* add file to cache */ 428 KdbpSymAddCachedFile(FileName, *RosSymInfo); 429 430 DPRINT("Installed symbols: %wZ %p\n", FileName, *RosSymInfo); 431 } 432 433 VOID 434 KdbSymProcessSymbols( 435 IN PLDR_DATA_TABLE_ENTRY LdrEntry) 436 { 437 if (!LoadSymbols) 438 { 439 LdrEntry->PatchInformation = NULL; 440 return; 441 } 442 443 /* Remove symbol info if it already exists */ 444 if (LdrEntry->PatchInformation) 445 KdbpSymRemoveCachedFile(LdrEntry->PatchInformation); 446 447 /* Load new symbol information */ 448 if (! RosSymCreateFromMem(LdrEntry->DllBase, 449 LdrEntry->SizeOfImage, 450 (PROSSYM_INFO*)&LdrEntry->PatchInformation)) 451 { 452 /* Error loading symbol info, try to load it from file */ 453 KdbpSymLoadModuleSymbols(&LdrEntry->FullDllName, 454 (PROSSYM_INFO*)&LdrEntry->PatchInformation); 455 456 /* It already added symbols to cache */ 457 } 458 else 459 { 460 /* Add file to cache */ 461 KdbpSymAddCachedFile(&LdrEntry->FullDllName, LdrEntry->PatchInformation); 462 } 463 464 DPRINT("Installed symbols: %wZ@%p-%p %p\n", 465 &LdrEntry->BaseDllName, 466 LdrEntry->DllBase, 467 (PVOID)(LdrEntry->SizeOfImage + (ULONG_PTR)LdrEntry->DllBase), 468 LdrEntry->PatchInformation); 469 470 } 471 472 VOID 473 NTAPI 474 KdbDebugPrint( 475 PCH Message, 476 ULONG Length) 477 { 478 /* Nothing here */ 479 } 480 481 482 /*! \brief Initializes the KDB symbols implementation. 483 * 484 * \param DispatchTable Pointer to the KD dispatch table 485 * \param BootPhase Phase of initialization 486 */ 487 VOID 488 NTAPI 489 KdbInitialize( 490 PKD_DISPATCH_TABLE DispatchTable, 491 ULONG BootPhase) 492 { 493 PCHAR p1, p2; 494 SHORT Found = FALSE; 495 CHAR YesNo; 496 PLDR_DATA_TABLE_ENTRY LdrEntry; 497 498 DPRINT("KdbSymInit() BootPhase=%d\n", BootPhase); 499 500 LoadSymbols = FALSE; 501 502 #if DBG 503 /* Load symbols only if we have 96Mb of RAM or more */ 504 if (MmNumberOfPhysicalPages >= 0x6000) 505 LoadSymbols = TRUE; 506 #endif 507 508 if (BootPhase == 0) 509 { 510 /* Write out the functions that we support for now */ 511 DispatchTable->KdpInitRoutine = KdpKdbgInit; 512 DispatchTable->KdpPrintRoutine = KdbDebugPrint; 513 514 /* Register as a Provider */ 515 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList); 516 517 /* Perform actual initialization of symbol module */ 518 //NtoskrnlModuleObject->PatchInformation = NULL; 519 //LdrHalModuleObject->PatchInformation = NULL; 520 521 InitializeListHead(&SymbolFileListHead); 522 KeInitializeSpinLock(&SymbolFileListLock); 523 524 /* Check the command line for /LOADSYMBOLS, /NOLOADSYMBOLS, 525 * /LOADSYMBOLS={YES|NO}, /NOLOADSYMBOLS={YES|NO} */ 526 ASSERT(KeLoaderBlock); 527 p1 = KeLoaderBlock->LoadOptions; 528 while('\0' != *p1 && NULL != (p2 = strchr(p1, '/'))) 529 { 530 p2++; 531 Found = 0; 532 if (0 == _strnicmp(p2, "LOADSYMBOLS", 11)) 533 { 534 Found = +1; 535 p2 += 11; 536 } 537 else if (0 == _strnicmp(p2, "NOLOADSYMBOLS", 13)) 538 { 539 Found = -1; 540 p2 += 13; 541 } 542 if (0 != Found) 543 { 544 while (isspace(*p2)) 545 { 546 p2++; 547 } 548 if ('=' == *p2) 549 { 550 p2++; 551 while (isspace(*p2)) 552 { 553 p2++; 554 } 555 YesNo = toupper(*p2); 556 if ('N' == YesNo || 'F' == YesNo || '0' == YesNo) 557 { 558 Found = -1 * Found; 559 } 560 } 561 LoadSymbols = (0 < Found); 562 } 563 p1 = p2; 564 } 565 566 RosSymInitKernelMode(); 567 } 568 else if (BootPhase == 1) 569 { 570 /* Load symbols for NTOSKRNL.EXE. 571 It is always the first module in PsLoadedModuleList. KeLoaderBlock can't be used here as its content is just temporary. */ 572 LdrEntry = CONTAINING_RECORD(PsLoadedModuleList.Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); 573 KdbSymProcessSymbols(LdrEntry); 574 575 /* Also load them for HAL.DLL. */ 576 LdrEntry = CONTAINING_RECORD(PsLoadedModuleList.Flink->Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); 577 KdbSymProcessSymbols(LdrEntry); 578 579 KdbpSymbolsInitialized = TRUE; 580 } 581 } 582 583 /* EOF */ 584