Debugging Tools for Windows

Mapping Symbols When the PEB is Paged Out

To load symbols, the debugger looks at the list of modules loaded by the operating system. The pointer to the user-mode module list is one of the items stored in the process environment block (PEB).

In order to reclaim memory, the Memory Manager may page out user-mode data to make space for other process or kernel mode components. The user-mode data that is paged out may include the PEB data structure. Without this data structure, the debugger cannot determine for which images to load symbols.

Note  This affects symbol files only for the user-mode modules. Kernel-mode modules and symbols are not affected, as they are tracked in a different list.

The following examples demonstrates how to use the !vad extension to map symbols when the PEB is paged out. The basic idea is to find the starting address and size of the relevant DLL so that you can then use the .reload command to load the necessary symbols.

Suppose that the address of the current process is 0xE0000126`01BA0AF0 and you want to fix the symbols for it. First, use the !process command to obtain the virtual address descriptor (VAD) root address:

kd> !process e000012601ba0af0 1
PROCESS e000012601ba0af0
    SessionId: 2  Cid: 0b50    Peb: 6fbfffde000  ParentCid: 0efc
    DirBase: 079e8461  ObjectTable: e000000600fbceb0  HandleCount: 360.
    Image: explorer.exe
    VadRoot e000012601a35e70 Vads 201 Clone 0 Private 917. Modified 2198. Locked 0.

Then use the !vad extension to list the VAD tree associated with the process. Those VADs labelled "EXECUTE_WRITECOPY" belong to DLLs.

kd> !vad e000012601a35e70
VAD     level      start      end    commit

e0000126019f9790 ( 6)      3fff0    3fff7        -1 Private      READONLY
e000012601be1080 ( 7)   37d9bd30 37d9bd3e         2 Mapped  Exe  EXECUTE_WRITECOPY           <-- these are DLLs
e000012600acd970 ( 5)   37d9bec0 37d9bece         2 Mapped  Exe  EXECUTE_WRITECOPY
e000012601a5cba0 ( 7)   37d9c910 37d9c924         2 Mapped  Exe  EXECUTE_WRITECOPY


Then use the !vad extension again to find the starting address and size of the paged out memory which holds the DLL of interest. This confirms that you have found the correct DLL:

kd> !vad e000012601be1080 1

VAD @ e000012601be1080
  Start VPN:      37d9bd30  End VPN: 37d9bd3e  Control Area:  e00001260197b8d0
  First ProtoPte: e0000006013e00a0  Last PTE fffffffffffffffc  Commit Charge         2 (2.)
  Secured.Flink          0  Blink           0  Banked/Extend:        0
  File Offset   0
   ImageMap ViewShare EXECUTE_WRITECOPY


        File: \Windows\System32\ExplorerFrame.dll

The "Start VPN" field – in this case, 0x37D9BD30 – indicates the starting virtual page number. This must be converted to an actual address, by multiplying it by the page size. You can use the ? (Evaluate Expression) command to multiply this value by 0x2000, which is the page size for the Itanium-based machine the example comes from.

kd> ? 37d9bd3e*2000        
Evaluate expression: 7676040298496 = 000006fb`37a7c000

Then the size of the range can be converted to bytes:

kd> ? 37d9bd3e-37d9bd30+1          <--   computes the number of pages
Evaluate expression: 15 = 00000000`0000000f
kd> ? f*2000
Evaluate expression: 122880 = 00000000`0001e000        

So ExplorerFrame.dll starts at address 0x000006Fb`37A7C000 and is 0x1E000 bytes large. You can load its symbols with:

kd> .reload /f ExplorerFrame.dll=6fb`37a7c000,1e000

Build machine: CAPEBUILD