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