Remove a warning about incorrect types, after discussion in channel it was decided...
[reactos.git] / reactos / ntoskrnl / dbg / kdb_symbols.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998-2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/dbg/kdb_symbols.c
22 * PURPOSE: Getting symbol information...
23 * PROGRAMMER: David Welch (welch@cwcom.net), ...
24 * REVISION HISTORY:
25 * ??/??/??: Created
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ddk/ntddk.h>
31 #include <roscfg.h>
32 #include <internal/ntoskrnl.h>
33 #include <internal/ke.h>
34 #include <internal/i386/segment.h>
35 #include <internal/i386/mm.h>
36 #include <internal/module.h>
37 #include <internal/mm.h>
38 #include <internal/ps.h>
39 #include <internal/trap.h>
40 #include <ntdll/ldr.h>
41 #include <internal/safe.h>
42 #include <internal/kd.h>
43 #include <rosrtl/string.h>
44
45 #define NDEBUG
46 #include <internal/debug.h>
47
48 #include "kdb.h"
49 #include "kdb_stabs.h"
50
51 /* GLOBALS ******************************************************************/
52
53 typedef struct _SYMBOLFILE_HEADER {
54 ULONG StabsOffset;
55 ULONG StabsLength;
56 ULONG StabstrOffset;
57 ULONG StabstrLength;
58 } SYMBOLFILE_HEADER, *PSYMBOLFILE_HEADER;
59
60 typedef struct _IMAGE_SYMBOL_INFO_CACHE {
61 LIST_ENTRY ListEntry;
62 ULONG RefCount;
63 UNICODE_STRING FileName;
64 PVOID FileBuffer;
65 PVOID SymbolsBase;
66 ULONG SymbolsLength;
67 PVOID SymbolStringsBase;
68 ULONG SymbolStringsLength;
69 } IMAGE_SYMBOL_INFO_CACHE, *PIMAGE_SYMBOL_INFO_CACHE;
70
71
72 static LIST_ENTRY SymbolFileListHead;
73 static KSPIN_LOCK SymbolFileListLock;
74
75
76 /* FUNCTIONS ****************************************************************/
77
78 /*! \brief Find a user-mode module...
79 *
80 * \param Address If \a Address is not NULL the module containing \a Address
81 * is searched.
82 * \param Name If \a Name is not NULL the module named \a Name will be
83 * searched.
84 * \param Index If \a Index is >= 0 the Index'th module will be returned.
85 * \param pInfo Pointer to a KDB_MODULE_INFO which is filled.
86 *
87 * \retval TRUE Module was found, \a pInfo was filled.
88 * \retval FALSE No module was found.
89 *
90 * \sa KdbpSymFindModule
91 */
92 STATIC BOOLEAN
93 KdbpSymFindUserModule(IN PVOID Address OPTIONAL,
94 IN LPCWSTR Name OPTIONAL,
95 IN INT Index OPTIONAL,
96 OUT PKDB_MODULE_INFO pInfo)
97 {
98 PLIST_ENTRY current_entry;
99 PLDR_MODULE current;
100 PEPROCESS CurrentProcess;
101 PPEB Peb = NULL;
102 INT Count = 0;
103
104 CurrentProcess = PsGetCurrentProcess();
105 if (CurrentProcess != NULL)
106 {
107 Peb = CurrentProcess->Peb;
108 }
109
110 if (Peb == NULL)
111 {
112 return FALSE;
113 }
114
115 current_entry = Peb->Ldr->InLoadOrderModuleList.Flink;
116
117 while (current_entry != &Peb->Ldr->InLoadOrderModuleList &&
118 current_entry != NULL)
119 {
120 current = CONTAINING_RECORD(current_entry, LDR_MODULE, InLoadOrderModuleList);
121
122 if ((Address != NULL && (Address >= (PVOID)current->BaseAddress &&
123 Address < (PVOID)((char *)current->BaseAddress + current->SizeOfImage))) ||
124 (Name != NULL && _wcsicmp(current->BaseDllName.Buffer, Name) == 0) ||
125 (Index >= 0 && Count++ == Index))
126 {
127 INT Length = current->BaseDllName.Length;
128 if (Length > 255)
129 Length = 255;
130 wcsncpy(pInfo->Name, current->BaseDllName.Buffer, Length);
131 pInfo->Name[Length] = L'\0';
132 pInfo->Base = (ULONG_PTR)current->BaseAddress;
133 pInfo->Size = current->SizeOfImage;
134 pInfo->SymbolInfo = &current->SymbolInfo;
135 return TRUE;
136 }
137 current_entry = current_entry->Flink;
138 }
139
140 return FALSE;
141 }
142
143 /*! \brief Find a kernel-mode module...
144 *
145 * Works like \a KdbpSymFindUserModule.
146 *
147 * \sa KdbpSymFindUserModule
148 */
149 STATIC BOOLEAN
150 KdbpSymFindModule(IN PVOID Address OPTIONAL,
151 IN LPCWSTR Name OPTIONAL,
152 IN INT Index OPTIONAL,
153 OUT PKDB_MODULE_INFO pInfo)
154 {
155 PLIST_ENTRY current_entry;
156 MODULE_TEXT_SECTION* current;
157 extern LIST_ENTRY ModuleTextListHead;
158 INT Count = 0;
159
160 current_entry = ModuleTextListHead.Flink;
161
162 while (current_entry != &ModuleTextListHead &&
163 current_entry != NULL)
164 {
165 current = CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry);
166
167 if ((Address != NULL && (Address >= (PVOID)current->Base &&
168 Address < (PVOID)(current->Base + current->Length))) ||
169 (Name != NULL && _wcsicmp(current->Name, Name) == 0) ||
170 (Index >= 0 && Count++ == Index))
171 {
172 wcsncpy(pInfo->Name, current->Name, 255);
173 pInfo->Name[255] = L'\0';
174 pInfo->Base = (ULONG_PTR)current->Base;
175 pInfo->Size = current->Length;
176 pInfo->SymbolInfo = &current->SymbolInfo;
177 return TRUE;
178 }
179 current_entry = current_entry->Flink;
180 }
181
182 return KdbpSymFindUserModule(Address, Name, Index-Count, pInfo);
183 }
184
185 /*! \brief Find module by address...
186 *
187 * \param Address Any address inside the module to look for.
188 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
189 * success.
190 *
191 * \retval TRUE Success - module found.
192 * \retval FALSE Failure - module not found.
193 *
194 * \sa KdbpSymFindModuleByName
195 * \sa KdbpSymFindModuleByIndex
196 */
197 BOOLEAN
198 KdbpSymFindModuleByAddress(IN PVOID Address,
199 OUT PKDB_MODULE_INFO pInfo)
200 {
201 return KdbpSymFindModule(Address, NULL, -1, pInfo);
202 }
203
204 /*! \brief Find module by name...
205 *
206 * \param Name Name of the module to look for.
207 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
208 * success.
209 *
210 * \retval TRUE Success - module found.
211 * \retval FALSE Failure - module not found.
212 *
213 * \sa KdbpSymFindModuleByAddress
214 * \sa KdbpSymFindModuleByIndex
215 */
216 BOOLEAN
217 KdbpSymFindModuleByName(IN LPCWSTR Name,
218 OUT PKDB_MODULE_INFO pInfo)
219 {
220 return KdbpSymFindModule(NULL, Name, -1, pInfo);
221 }
222
223 /*! \brief Find module by index...
224 *
225 * \param Index Index of the module to return.
226 * \param pInfo Pointer to a KDB_MODULE_INFO struct which is filled on
227 * success.
228 *
229 * \retval TRUE Success - module found.
230 * \retval FALSE Failure - module not found.
231 *
232 * \sa KdbpSymFindModuleByName
233 * \sa KdbpSymFindModuleByAddress
234 */
235 BOOLEAN
236 KdbpSymFindModuleByIndex(IN INT Index,
237 OUT PKDB_MODULE_INFO pInfo)
238 {
239 return KdbpSymFindModule(NULL, NULL, Index, pInfo);
240 }
241
242 /*! \brief Print address...
243 *
244 * Tries to lookup line number, file name and function name for the given
245 * address and prints it.
246 * If no such information is found the address is printed in the format
247 * <module: offset>, otherwise the format will be
248 * <module: offset (filename:linenumber (functionname))>
249 *
250 * \retval TRUE Module containing \a Address was found, \a Address was printed.
251 * \retval FALSE No module containing \a Address was found, nothing was printed.
252 */
253 BOOLEAN
254 KdbSymPrintAddress(IN PVOID Address)
255 {
256 KDB_MODULE_INFO Info;
257 ULONG_PTR RelativeAddress;
258 NTSTATUS Status;
259 ULONG LineNumber;
260 CHAR FileName[256];
261 CHAR FunctionName[256];
262
263 if (!KdbpSymFindModuleByAddress(Address, &Info))
264 return FALSE;
265
266 RelativeAddress = (ULONG_PTR) Address - Info.Base;
267 Status = KdbSymGetAddressInformation(Info.SymbolInfo,
268 RelativeAddress,
269 &LineNumber,
270 FileName,
271 FunctionName);
272 if (NT_SUCCESS(Status))
273 {
274 DbgPrint("<%ws: %x (%s:%d (%s))>",
275 Info.Name, RelativeAddress, FileName, LineNumber, FunctionName);
276 }
277 else
278 {
279 DbgPrint("<%ws: %x>", Info.Name, RelativeAddress);
280 }
281
282 return TRUE;
283 }
284
285
286 /*! \brief Get information for an address (source file, line number,
287 * function name)
288 *
289 * \param SymbolInfo Pointer to IMAGE_SYMBOL_INFO.
290 * \param RelativeAddress Relative address to look up.
291 * \param LineNumber Pointer to an ULONG which is filled with the line
292 * number (can be NULL)
293 * \param FileName Pointer to an array of CHARs which gets filled with
294 * the filename (can be NULL)
295 * \param FunctionName Pointer to an array of CHARs which gets filled with
296 * the function name (can be NULL)
297 *
298 * \returns NTSTATUS error code.
299 * \retval STATUS_SUCCESS At least one of the requested informations was found.
300 * \retval STATUS_UNSUCCESSFUL None of the requested information was found.
301 */
302 NTSTATUS
303 KdbSymGetAddressInformation(IN PIMAGE_SYMBOL_INFO SymbolInfo,
304 IN ULONG_PTR RelativeAddress,
305 OUT PULONG LineNumber OPTIONAL,
306 OUT PCH FileName OPTIONAL,
307 OUT PCH FunctionName OPTIONAL)
308 {
309 PSTAB_ENTRY FunctionEntry = NULL, FileEntry = NULL, LineEntry = NULL;
310
311 DPRINT("RelativeAddress = 0x%08x\n", RelativeAddress);
312
313 if (SymbolInfo->SymbolsBase == NULL || SymbolInfo->SymbolsLength == 0 ||
314 SymbolInfo->SymbolStringsBase == NULL || SymbolInfo->SymbolStringsLength == 0)
315 {
316 return STATUS_UNSUCCESSFUL;
317 }
318
319 #ifdef PEDANTIC_STABS
320 if (RelativeAddress >= SymbolInfo->ImageSize)
321 {
322 DPRINT("Address is not within .text section. RelativeAddress %p Length 0x%x\n",
323 RelativeAddress, SymbolInfo->ImageSize);
324 return STATUS_UNSUCCESSFUL;
325 }
326 #endif
327
328 ASSERT(LineNumber || FileName || FunctionName);
329
330 if (LineNumber != NULL || FunctionName != NULL)
331 {
332 /* find stab entry for function */
333 FunctionEntry = KdbpStabFindEntry(SymbolInfo, N_FUN, (PVOID)RelativeAddress, NULL);
334 if (FunctionEntry == NULL)
335 {
336 DPRINT("No function stab entry found. RelativeAddress %p\n", RelativeAddress);
337 }
338
339 if (LineNumber != NULL && FunctionEntry != NULL)
340 {
341 /* find stab entry for line number */
342 ULONG_PTR FunctionRelativeAddress = FunctionEntry->n_value - (ULONG_PTR)SymbolInfo->ImageBase;
343 ULONG_PTR AddrFound = 0;
344 PSTAB_ENTRY NextLineEntry;
345
346 LineEntry = NextLineEntry = FunctionEntry;
347 while (NextLineEntry != NULL)
348 {
349 NextLineEntry++;
350 if ((ULONG_PTR)NextLineEntry >= ((ULONG_PTR)SymbolInfo->SymbolsBase + SymbolInfo->SymbolsLength))
351 break;
352 if (NextLineEntry->n_type == N_FUN)
353 break;
354 if (NextLineEntry->n_type != N_SLINE)
355 continue;
356
357 if (((NextLineEntry->n_value+FunctionRelativeAddress) <= RelativeAddress) &&
358 (NextLineEntry->n_value > AddrFound))
359 {
360 AddrFound = NextLineEntry->n_value;
361 LineEntry = NextLineEntry;
362 }
363 }
364 }
365 }
366
367 if (FileName != NULL)
368 {
369 /* find stab entry for file name */
370 PCHAR p;
371 INT Length;
372
373 FileEntry = KdbpStabFindEntry(SymbolInfo, N_SO, (PVOID)RelativeAddress, NULL);
374 if (FileEntry != NULL)
375 {
376 p = (PCHAR)SymbolInfo->SymbolStringsBase + FileEntry->n_strx;
377 Length = strlen(p);
378 if (p[Length - 1] == '/' || p[Length - 1] == '\\') /* source dir */
379 FileEntry = KdbpStabFindEntry(SymbolInfo, N_SO, (PVOID)RelativeAddress, FileEntry + 1);
380 }
381 if (FileEntry == NULL)
382 {
383 DPRINT("No filename stab entry found. RelativeAddress %p\n", RelativeAddress);
384 }
385 }
386
387 if (((LineNumber != NULL && LineEntry == NULL) || LineNumber == NULL) &&
388 ((FileName != NULL && FileEntry == NULL) || FileName == NULL) &&
389 ((FunctionName != NULL && FunctionEntry == NULL) || FunctionName == NULL))
390 {
391 DPRINT("None of the requested information was found!\n");
392 return STATUS_UNSUCCESSFUL;
393 }
394
395 if (LineNumber != NULL)
396 {
397 *LineNumber = 0;
398 if (LineEntry != NULL)
399 *LineNumber = LineEntry->n_desc;
400 }
401 if (FileName != NULL)
402 {
403 PCHAR Name = "";
404 if (FileEntry != NULL)
405 {
406 Name = (PCHAR)SymbolInfo->SymbolStringsBase + FileEntry->n_strx;
407 }
408 strcpy(FileName, Name);
409 }
410 if (FunctionName != NULL)
411 {
412 PCHAR Name = "", p;
413 if (FunctionEntry != NULL)
414 Name = (PCHAR)SymbolInfo->SymbolStringsBase + FunctionEntry->n_strx;
415 strcpy(FunctionName, Name);
416 if ((p = strchr(FunctionName, ':')) != NULL) /* remove extra info from function name */
417 *p = '\0';
418 }
419
420 return STATUS_SUCCESS;
421 }
422
423
424 /*! \brief Get absolute source-line number or function address
425 *
426 * \param SymbolInfo IMAGE_SYMBOL_INFO of the module containing source file/line number.
427 * \param FileName Source filename.
428 * \param LineNumber Line number in source file.
429 * \param FuncName Function name.
430 * \param Address Filled with the address on success.
431 *
432 * \retval TRUE Success.
433 * \retval FALSE Failure.
434 */
435 BOOLEAN
436 KdbpSymGetSourceAddress(IN PIMAGE_SYMBOL_INFO SymbolInfo,
437 IN PCHAR FileName,
438 IN ULONG LineNumber OPTIONAL,
439 IN PCHAR FuncName OPTIONAL,
440 OUT PVOID *Address)
441 {
442 PSTAB_ENTRY Entry, FunctionEntry = NULL;
443 PCHAR SymbolName, p;
444 CHAR Buffer[512] = "";
445 INT Length, FileNameLength, FuncNameLength = 0;
446
447 if (FuncName == NULL && LineNumber < 1)
448 return FALSE;
449
450 FileNameLength = strlen(FileName);
451 FuncNameLength = strlen(FuncName);
452 for (Entry = SymbolInfo->SymbolsBase;
453 (ULONG_PTR)Entry < (ULONG_PTR)(SymbolInfo->SymbolsBase + SymbolInfo->SymbolsLength);
454 Entry++)
455 {
456 if (Entry->n_type != N_SO)
457 continue;
458
459 SymbolName = (PCHAR)SymbolInfo->SymbolStringsBase + Entry->n_strx;
460 Length = strlen(SymbolName);
461 if (SymbolName[Length - 1] == '/' ||
462 SymbolName[Length - 1] == '\\')
463 {
464 strncpy(Buffer, SymbolName, sizeof (Buffer) - 1);
465 Buffer[sizeof (Buffer) - 1] = '\0';
466 continue;
467 }
468 strncat(Buffer, SymbolName, sizeof (Buffer) - 1);
469 Buffer[sizeof (Buffer) - 1] = '\0';
470
471 Length = strlen(Buffer);
472 if (strcmp(Buffer + Length - FileNameLength, FileName) != 0)
473 continue;
474
475 Entry++;
476 for (;(ULONG_PTR)Entry < (ULONG_PTR)(SymbolInfo->SymbolsBase + SymbolInfo->SymbolsLength);
477 Entry++)
478 {
479 if (Entry->n_type == N_FUN)
480 FunctionEntry = Entry;
481 else if (Entry->n_type == N_SO)
482 break;
483 else if (Entry->n_type != N_SLINE || LineNumber < 1)
484 continue;
485
486 if (LineNumber > 0 && Entry->n_desc != LineNumber)
487 continue;
488 else /* if (FunctionName != NULL) */
489 {
490 SymbolName = (PCHAR)SymbolInfo->SymbolStringsBase + Entry->n_strx;
491 p = strchr(SymbolName, ':');
492 if (p == NULL)
493 return FALSE;
494 Length = p - SymbolName;
495 if (Length != FuncNameLength)
496 continue;
497 if (strncmp(FuncName, SymbolName, Length) != 0)
498 continue;
499 }
500
501 /* found */
502 if (Entry->n_type == N_FUN)
503 {
504 *Address = (PVOID)Entry->n_value; /* FIXME: relocate address */
505 return TRUE;
506 }
507
508 if (FunctionEntry == NULL)
509 return FALSE;
510
511 *Address = (PVOID)((ULONG_PTR)Entry->n_value + FunctionEntry->n_value); /* FIXME: relocate address */
512 return TRUE;
513 }
514 break;
515 }
516
517 return FALSE;
518 }
519
520 /*! \brief Find cached symbol file.
521 *
522 * Looks through the list of cached symbol files and tries to find an already
523 * loaded one.
524 *
525 * \param FileName FileName of the symbol file to look for.
526 *
527 * \returns A pointer to the cached symbol info.
528 * \retval NULL No cached info found.
529 *
530 * \sa KdbpSymAddCachedFile
531 */
532 STATIC PIMAGE_SYMBOL_INFO_CACHE
533 KdbpSymFindCachedFile(IN PUNICODE_STRING FileName)
534 {
535 PIMAGE_SYMBOL_INFO_CACHE Current;
536 PLIST_ENTRY CurrentEntry;
537 KIRQL Irql;
538
539 DPRINT("Looking for cached symbol file %wZ\n", FileName);
540
541 KeAcquireSpinLock(&SymbolFileListLock, &Irql);
542
543 CurrentEntry = SymbolFileListHead.Flink;
544 while (CurrentEntry != (&SymbolFileListHead))
545 {
546 Current = CONTAINING_RECORD(CurrentEntry, IMAGE_SYMBOL_INFO_CACHE, ListEntry);
547
548 DPRINT("Current->FileName %wZ FileName %wZ\n", &Current->FileName, FileName);
549 if (RtlEqualUnicodeString(&Current->FileName, FileName, TRUE))
550 {
551 KeReleaseSpinLock(&SymbolFileListLock, Irql);
552 DPRINT("Found cached file!\n");
553 return Current;
554 }
555
556 CurrentEntry = CurrentEntry->Flink;
557 }
558
559 KeReleaseSpinLock(&SymbolFileListLock, Irql);
560
561 DPRINT("Cached file not found!\n");
562 return NULL;
563 }
564
565 /*! \brief Add a symbol file to the cache.
566 *
567 * \param FileName Filename of the symbol file.
568 * \param SymbolInfo Pointer to the symbol info.
569 *
570 * \sa KdbpSymRemoveCachedFile
571 */
572 STATIC VOID
573 KdbpSymAddCachedFile(IN PUNICODE_STRING FileName,
574 IN PIMAGE_SYMBOL_INFO SymbolInfo)
575 {
576 PIMAGE_SYMBOL_INFO_CACHE CacheEntry;
577
578 DPRINT("Adding symbol file: FileBuffer = %p, ImageBase = %p\n",
579 SymbolInfo->FileBuffer, SymbolInfo->ImageBase);
580
581 /* allocate entry */
582 CacheEntry = ExAllocatePool(NonPagedPool, sizeof (IMAGE_SYMBOL_INFO_CACHE));
583 ASSERT(CacheEntry);
584 RtlZeroMemory(CacheEntry, sizeof (IMAGE_SYMBOL_INFO_CACHE));
585
586 /* fill entry */
587 RtlCreateUnicodeString(&CacheEntry->FileName, FileName->Buffer);
588 ASSERT(CacheEntry->FileName.Buffer);
589 CacheEntry->RefCount = 1;
590 CacheEntry->FileBuffer = SymbolInfo->FileBuffer;
591 CacheEntry->SymbolsBase = SymbolInfo->SymbolsBase;
592 CacheEntry->SymbolsLength = SymbolInfo->SymbolsLength;
593 CacheEntry->SymbolStringsBase = SymbolInfo->SymbolStringsBase;
594 CacheEntry->SymbolStringsLength = SymbolInfo->SymbolStringsLength;
595 InsertTailList(&SymbolFileListHead, &CacheEntry->ListEntry); /* FIXME: Lock list? */
596 }
597
598 /*! \brief Remove a symbol file (reference) from the cache.
599 *
600 * Tries to find a cache entry matching the given symbol info and decreases
601 * it's reference count. If the refcount is 0 after decreasing it the cache
602 * entry will be removed from the list and freed.
603 *
604 * \param SymbolInfo Pointer to the symbol info.
605 *
606 * \sa KdbpSymAddCachedFile
607 */
608 STATIC VOID
609 KdbpSymRemoveCachedFile(IN PIMAGE_SYMBOL_INFO SymbolInfo)
610 {
611 PIMAGE_SYMBOL_INFO_CACHE Current;
612 PLIST_ENTRY CurrentEntry;
613 KIRQL Irql;
614
615 KeAcquireSpinLock(&SymbolFileListLock, &Irql);
616
617 CurrentEntry = SymbolFileListHead.Flink;
618 while (CurrentEntry != (&SymbolFileListHead))
619 {
620 Current = CONTAINING_RECORD(CurrentEntry, IMAGE_SYMBOL_INFO_CACHE, ListEntry);
621
622 if (Current->FileBuffer == SymbolInfo->FileBuffer) /* found */
623 {
624 ASSERT(Current->RefCount > 0);
625 Current->RefCount--;
626 if (Current->RefCount < 1)
627 {
628 RemoveEntryList(&Current->ListEntry);
629 ExFreePool(Current->FileBuffer);
630 ExFreePool(Current);
631 }
632 KeReleaseSpinLock(&SymbolFileListLock, Irql);
633 return;
634 }
635
636 CurrentEntry = CurrentEntry->Flink;
637 }
638
639 KeReleaseSpinLock(&SymbolFileListLock, Irql);
640 DPRINT1("Warning: Removing unknown symbol file: FileBuffer = %p, ImageBase = %p\n",
641 SymbolInfo->FileBuffer, SymbolInfo->ImageBase);
642 ASSERT(0);
643 }
644
645 /*! \brief Loads a symbol file.
646 *
647 * \param FileName Filename of the symbol file to load.
648 * \param SymbolInfo Pointer to a SymbolInfo which gets filled.
649 *
650 * \sa KdbpSymUnloadModuleSymbols
651 */
652 STATIC VOID
653 KdbpSymLoadModuleSymbols(IN PUNICODE_STRING FileName,
654 OUT PIMAGE_SYMBOL_INFO SymbolInfo)
655 {
656 FILE_STANDARD_INFORMATION FileStdInfo;
657 OBJECT_ATTRIBUTES ObjectAttributes;
658 WCHAR TmpFileName[MAX_PATH];
659 UNICODE_STRING SymFileName;
660 LPWSTR Start, Ext;
661 HANDLE FileHandle;
662 PVOID FileBuffer;
663 NTSTATUS Status;
664 ULONG Length;
665 IO_STATUS_BLOCK IoStatusBlock;
666 PSYMBOLFILE_HEADER SymbolFileHeader;
667 PIMAGE_SYMBOL_INFO_CACHE CachedSymbolFile;
668
669 /* Get the path to the symbol store */
670 wcscpy(TmpFileName, L"\\SystemRoot\\symbols\\");
671
672 /* Get the symbol filename from the module name */
673 Start = wcsrchr(FileName->Buffer, L'\\');
674 if (Start == NULL)
675 Start = FileName->Buffer;
676 else
677 Start++;
678
679 Ext = wcsrchr(FileName->Buffer, L'.');
680 if (Ext != NULL)
681 Length = Ext - Start;
682 else
683 Length = wcslen(Start);
684
685 wcsncat(TmpFileName, Start, Length);
686 wcscat(TmpFileName, L".sym");
687 RtlInitUnicodeString(&SymFileName, TmpFileName);
688
689 /* Try to find cached (already loaded) symbol file */
690 CachedSymbolFile = KdbpSymFindCachedFile(&SymFileName);
691 if (CachedSymbolFile != NULL)
692 {
693 DPRINT("Found cached symbol file %wZ\n", &SymFileName);
694 CachedSymbolFile->RefCount++;
695 SymbolInfo->FileBuffer = CachedSymbolFile->FileBuffer;
696 SymbolInfo->SymbolsBase = CachedSymbolFile->SymbolsBase;
697 SymbolInfo->SymbolsLength = CachedSymbolFile->SymbolsLength;
698 SymbolInfo->SymbolStringsBase = CachedSymbolFile->SymbolStringsBase;
699 SymbolInfo->SymbolStringsLength = CachedSymbolFile->SymbolStringsLength;
700 return;
701 }
702
703 /* Open the file */
704 InitializeObjectAttributes(&ObjectAttributes,
705 &SymFileName,
706 0,
707 NULL,
708 NULL);
709
710 DPRINT("Attempting to open symbols: %wZ\n", &SymFileName);
711
712 Status = ZwOpenFile(&FileHandle,
713 FILE_ALL_ACCESS,
714 &ObjectAttributes,
715 &IoStatusBlock,
716 0,
717 FILE_SYNCHRONOUS_IO_NONALERT|FILE_NO_INTERMEDIATE_BUFFERING);
718 if (!NT_SUCCESS(Status))
719 {
720 DPRINT("Could not open symbol file: %wZ\n", &SymFileName);
721 return;
722 }
723
724 DPRINT("Loading symbols from %wZ...\n", &SymFileName);
725
726 /* Get the size of the file */
727 Status = ZwQueryInformationFile(FileHandle,
728 &IoStatusBlock,
729 &FileStdInfo,
730 sizeof(FileStdInfo),
731 FileStandardInformation);
732 if (!NT_SUCCESS(Status))
733 {
734 DPRINT("Could not get file size\n");
735 ZwClose(FileHandle);
736 return;
737 }
738
739 DPRINT("Symbol file is %08x bytes\n", FileStdInfo.EndOfFile.u.LowPart);
740
741 /* Allocate nonpageable memory for symbol file */
742 FileBuffer = ExAllocatePool(NonPagedPool,
743 FileStdInfo.AllocationSize.u.LowPart);
744
745 if (FileBuffer == NULL)
746 {
747 DPRINT("Could not allocate memory for symbol file\n");
748 ZwClose(FileHandle);
749 return;
750 }
751
752 /* Load file into memory chunk */
753 Status = ZwReadFile(FileHandle,
754 0, 0, 0,
755 &IoStatusBlock,
756 FileBuffer,
757 FileStdInfo.EndOfFile.u.LowPart,
758 0, 0);
759 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
760 {
761 DPRINT("Could not read symbol file into memory (Status 0x%x)\n", Status);
762 ExFreePool(FileBuffer);
763 ZwClose(FileHandle);
764 return;
765 }
766
767 ZwClose(FileHandle);
768
769 DPRINT("Symbols loaded.\n");
770
771 SymbolFileHeader = (PSYMBOLFILE_HEADER) FileBuffer;
772 SymbolInfo->FileBuffer = FileBuffer;
773 SymbolInfo->SymbolsBase = FileBuffer + SymbolFileHeader->StabsOffset;
774 SymbolInfo->SymbolsLength = SymbolFileHeader->StabsLength;
775 SymbolInfo->SymbolStringsBase = FileBuffer + SymbolFileHeader->StabstrOffset;
776 SymbolInfo->SymbolStringsLength = SymbolFileHeader->StabstrLength;
777
778 /* add file to cache */
779 KdbpSymAddCachedFile(&SymFileName, SymbolInfo);
780
781 DPRINT("Installed stabs: %wZ (%08x-%08x,%08x)\n",
782 FileName,
783 SymbolInfo->SymbolsBase,
784 SymbolInfo->SymbolsLength + SymbolInfo->SymbolsBase,
785 SymbolInfo->SymbolStringsBase);
786 }
787
788 /*! \brief Unloads symbol info.
789 *
790 * \param SymbolInfo Pointer to the symbol info to unload.
791 *
792 * \sa KdbpSymLoadModuleSymbols
793 */
794 STATIC VOID
795 KdbpSymUnloadModuleSymbols(IN PIMAGE_SYMBOL_INFO SymbolInfo)
796 {
797 DPRINT("Unloading symbols\n");
798
799 if (SymbolInfo != NULL && SymbolInfo->FileBuffer != NULL)
800 {
801 KdbpSymRemoveCachedFile(SymbolInfo);
802 SymbolInfo->FileBuffer = NULL;
803 SymbolInfo->SymbolsBase = NULL;
804 SymbolInfo->SymbolsLength = 0;
805 }
806 }
807
808 /*! \brief Load symbol info for a user module.
809 *
810 * \param LdrModule Pointer to the module to load symbols for.
811 */
812 VOID
813 KdbSymLoadUserModuleSymbols(IN PLDR_MODULE LdrModule)
814 {
815 DPRINT("LdrModule %p\n", LdrModule);
816
817 RtlZeroMemory(&LdrModule->SymbolInfo, sizeof (LdrModule->SymbolInfo));
818 LdrModule->SymbolInfo.ImageBase = (ULONG_PTR)LdrModule->BaseAddress;
819 LdrModule->SymbolInfo.ImageSize = LdrModule->SizeOfImage;
820
821 KdbpSymLoadModuleSymbols(&LdrModule->FullDllName, &LdrModule->SymbolInfo);
822 }
823
824 /*! \brief Frees all symbols loaded for a process.
825 *
826 * \param Process Pointer to a process.
827 */
828 VOID
829 KdbSymFreeProcessSymbols(IN PEPROCESS Process)
830 {
831 PLIST_ENTRY CurrentEntry;
832 PLDR_MODULE Current;
833 PIMAGE_SYMBOL_INFO SymbolInfo;
834 PEPROCESS CurrentProcess;
835 PPEB Peb;
836
837 CurrentProcess = PsGetCurrentProcess();
838 if (CurrentProcess != Process)
839 {
840 KeAttachProcess(&Process->Pcb);
841 }
842 Peb = Process->Peb;
843 ASSERT(Peb);
844 ASSERT(Peb->Ldr);
845
846 CurrentEntry = Peb->Ldr->InLoadOrderModuleList.Flink;
847 while (CurrentEntry != &Peb->Ldr->InLoadOrderModuleList &&
848 CurrentEntry != NULL)
849 {
850 Current = CONTAINING_RECORD(CurrentEntry, LDR_MODULE, InLoadOrderModuleList);
851
852 SymbolInfo = &Current->SymbolInfo;
853 KdbpSymUnloadModuleSymbols(SymbolInfo);
854
855 CurrentEntry = CurrentEntry->Flink;
856 }
857 if (CurrentProcess != Process)
858 {
859 KeDetachProcess();
860 }
861 }
862
863 /*! \brief Load symbol info for a driver.
864 *
865 * \param Filename Filename of the driver.
866 * \param Module Pointer to the driver MODULE_OBJECT.
867 */
868 VOID
869 KdbSymLoadDriverSymbols(IN PUNICODE_STRING Filename,
870 IN PMODULE_OBJECT Module)
871 {
872 /* Load symbols for the image if available */
873 DPRINT("Loading driver %wZ symbols (driver @ %08x)\n", Filename, Module->Base);
874
875 RtlZeroMemory(&Module->TextSection->SymbolInfo, sizeof (Module->TextSection->SymbolInfo));
876 Module->TextSection->SymbolInfo.ImageBase = Module->TextSection->Base;
877 Module->TextSection->SymbolInfo.ImageSize = Module->TextSection->Length;
878
879 KdbpSymLoadModuleSymbols(Filename, &Module->TextSection->SymbolInfo);
880 }
881
882 /*! \brief Unloads symbol info for a driver.
883 *
884 * \param ModuleObject Pointer to the driver MODULE_OBJECT.
885 */
886 VOID
887 KdbSymUnloadDriverSymbols(IN PMODULE_OBJECT ModuleObject)
888 {
889 /* Unload symbols for module if available */
890 KdbpSymUnloadModuleSymbols(&ModuleObject->TextSection->SymbolInfo);
891 }
892
893 /*! \brief Called when a symbol file is loaded by the loader?
894 *
895 * Tries to find a driver (.sys) or executable (.exe) with the same base name
896 * as the symbol file and sets the drivers/exes symbol info to the loaded
897 * module.
898 * Used to load ntoskrnl and hal symbols before the SystemRoot is available to us.
899 *
900 * \param ModuleLoadBase Base address of the loaded symbol file.
901 * \param FileName Filename of the symbol file.
902 * \param Length Length of the loaded symbol file/module.
903 */
904 VOID
905 KdbSymProcessSymbolFile(IN PVOID ModuleLoadBase,
906 IN PCHAR FileName,
907 IN ULONG Length)
908 {
909 PMODULE_OBJECT ModuleObject;
910 UNICODE_STRING ModuleName;
911 CHAR TmpBaseName[MAX_PATH];
912 CHAR TmpFileName[MAX_PATH];
913 PSYMBOLFILE_HEADER SymbolFileHeader;
914 PIMAGE_SYMBOL_INFO SymbolInfo;
915 ANSI_STRING AnsiString;
916 PCHAR Extension;
917
918 DPRINT("Module %s is a symbol file\n", FileName);
919
920 strncpy(TmpBaseName, FileName, MAX_PATH-1);
921 TmpBaseName[MAX_PATH-1] = '\0';
922 /* remove the extension '.sym' */
923 Extension = strrchr(TmpBaseName, '.');
924 if (Extension && 0 == _stricmp(Extension, ".sym"))
925 {
926 *Extension = 0;
927 }
928
929 DPRINT("base: %s (Length %d)\n", TmpBaseName, Length);
930
931 strcpy(TmpFileName, TmpBaseName);
932 strcat(TmpFileName, ".sys");
933 RtlInitAnsiString(&AnsiString, TmpFileName);
934
935 RtlAnsiStringToUnicodeString(&ModuleName, &AnsiString, TRUE);
936 ModuleObject = LdrGetModuleObject(&ModuleName);
937 RtlFreeUnicodeString(&ModuleName);
938 if (ModuleObject == NULL)
939 {
940 strcpy(TmpFileName, TmpBaseName);
941 strcat(TmpFileName, ".exe");
942 RtlInitAnsiString(&AnsiString, TmpFileName);
943 RtlAnsiStringToUnicodeString(&ModuleName, &AnsiString, TRUE);
944 ModuleObject = LdrGetModuleObject(&ModuleName);
945 RtlFreeUnicodeString(&ModuleName);
946 }
947 if (ModuleObject != NULL)
948 {
949 SymbolInfo = (PIMAGE_SYMBOL_INFO) &ModuleObject->TextSection->SymbolInfo;
950 if (SymbolInfo->FileBuffer != NULL)
951 {
952 KdbpSymRemoveCachedFile(SymbolInfo);
953 }
954
955 SymbolFileHeader = (PSYMBOLFILE_HEADER) ModuleLoadBase;
956 SymbolInfo->FileBuffer = ModuleLoadBase;
957 SymbolInfo->SymbolsBase = ModuleLoadBase + SymbolFileHeader->StabsOffset;
958 SymbolInfo->SymbolsLength = SymbolFileHeader->StabsLength;
959 SymbolInfo->SymbolStringsBase = ModuleLoadBase + SymbolFileHeader->StabstrOffset;
960 SymbolInfo->SymbolStringsLength = SymbolFileHeader->StabstrLength;
961 DPRINT("Installed stabs: %s@%08x-%08x (%08x-%08x,%08x)\n",
962 FileName,
963 ModuleObject->Base, ModuleObject->Length + ModuleObject->Base,
964 SymbolInfo->SymbolsBase,
965 SymbolInfo->SymbolsLength + SymbolInfo->SymbolsBase,
966 SymbolInfo->SymbolStringsBase);
967 }
968 }
969
970 /*! \brief Initializes the KDB symbols implementation.
971 *
972 * \param NtoskrnlTextSection MODULE_TEXT_SECTION of ntoskrnl.exe
973 * \param LdrHalTextSection MODULE_TEXT_SECTION of hal.sys
974 */
975 VOID
976 KdbSymInit(IN PMODULE_TEXT_SECTION NtoskrnlTextSection,
977 IN PMODULE_TEXT_SECTION LdrHalTextSection)
978 {
979 RtlZeroMemory(&NtoskrnlTextSection->SymbolInfo, sizeof(NtoskrnlTextSection->SymbolInfo));
980 NtoskrnlTextSection->SymbolInfo.ImageBase = NtoskrnlTextSection->OptionalHeader->ImageBase;
981 NtoskrnlTextSection->SymbolInfo.ImageSize = NtoskrnlTextSection->Length;
982
983 RtlZeroMemory(&LdrHalTextSection->SymbolInfo, sizeof(LdrHalTextSection->SymbolInfo));
984 LdrHalTextSection->SymbolInfo.ImageBase = LdrHalTextSection->OptionalHeader->ImageBase;
985 LdrHalTextSection->SymbolInfo.ImageSize = LdrHalTextSection->Length;
986
987 InitializeListHead(&SymbolFileListHead);
988 KeInitializeSpinLock(&SymbolFileListLock);
989 }
990
991 /* EOF */