How to debug missing imports at driver load time

Debugging when your driver fails to load can be exasperating, especially if it is due to a missing import.  Windows 2000 would put up a dialog box telling the user which import was missing, but the user can’t do anything about it (unless she is the driver developer), so that dialog was removed post Windows 2000.  You can run depends.exe against your driver on the OS that you are targeting, but that’s no fun ;).  Instead, let’s see you how can debug unresolved imports at load time. 

I added a call to KeEnterGuardedRegion into my test driver wdfrawbusenumtest.  This function was added in Windows Server 2003 SP1, so it is a perfect API to import and then try to load on XP.  First, you must set your symbols to the public symbol server

0: kd> !symfix
0: kd> .sympath
Symbol search path is: srv*

0: kd> .reload ntoskrnl.exe
Force unload of ntkrnlmp.exe
Loading symbols for 804d7000     ntkrnlmp.exe ->   ntkrnlmp.exe
ModLoad: 804d7000 806fd000   ntkrnlmp.exe

0: kd> !lmi nt

    Symbol Type: PDB      – Symbols loaded successfully from symbol server.
    Load Report: public symbols , not source indexed

then set a bp on MmLoadSystemImage
0: kd> bp nt!MmLoadSystemImage
0: kd> g

then attempt to load the driver (I used device manager, you can use devcon or net start if you are writing a legacy style driver which does not support PnP).  And we hit our breakpoint

Breakpoint 0 hit
805a86ff 6874010000       push    0x174
0: kd> kb
ChildEBP RetAddr  Args to Child             
f88f2820 805a40d2 f88f28a4 00000000 00000000 nt!MmLoadSystemImage

The first parameter passed to MmLoadSystemImage is a PUNICODE_STRING, so let’s make sure that this is our driver we are going to debug

0: kd> dt f88f28a4 _UNICODE_STRING
   +0x000 Length           : 0x64
   +0x002 MaximumLength    : 0x66
   +0x004 Buffer           : 0xe1600c78  “\SystemRoot\system32\DRIVERS\wdfrawbusenumtest.sys”

Now, we need to set a breakpoint on MiResolveImageReferences, which will return the unresolved import on failure.

0: kd> bp nt!MiResolveImageReferences
0: kd> g

805a223d 8bff             mov     edi,edi
0: kd> kb
ChildEBP RetAddr  Args to Child             
f88f2670 805a3ecf f1e47000 f88f27c4 00000000 nt!MiResolveImageReferences
f88f2820 805a40d2 f88f28a4 00000000 00000000 nt!MmLoadSystemImage+0x8c8

This is an internal function so this can change at anytime, but currently the fourth parameter is a PCHAR* which will be filled with name of the resolved import on failure.  First, we will look at the 4th (on Windows 2000 it is the 5th, but we are debugging XP here) parameter by dumping the EBP and then looking at what it points to (since it is a PCHAR*).

0: kd> dp f88f2670
f88f2670  00000246 805a3ecf f1e47000 f88f27c4
f88f2680  00000000 f88f27d4 f88f27d8 f88f27dc

0: kd> dp f88f27d4 l1
f88f27d4  81ffa250

and then we will jump back to the caller and verify failure

0: kd> g 805a3ecf
0: kd> reax
0: kd> !error c0000263
Error code: (NTSTATUS) 0xc0000263 (3221226083) – {Driver Entry Point Not Found}  The %hs device driver could not locate the entry point %hs in driver %hs.

and now let’s see the missing import!

0: kd> dc 81ffa250
81ffa250  6e45654b 47726574 64726175 65526465  KeEnterGuardedRe
81ffa260  6e6f6967 00000000 00000000 00000000  gion…………

Comments (8)

  1. BryanK says:

    Er, wait a minute here…

    > Windows 2000 would put up a dialog box telling the user which import was missing, but the user can’t do anything about it (unless she is the driver developer), so that dialog was removed post Windows 2000.

    And so the user can no longer tell anyone (including the real driver developer) what the missing symbol was, either.  Now the driver developer needs either symbols for all OS versions or access to the public symbol server, knowledge of system internals (the existence of MiResolveImageReferences and its private parameters), and a debugger?  (Or a copy of the target OS and depends.exe.)

    I guess I don’t see how this is a win.  Users shouldn’t ever see that error unless the driver testing people haven’t done their job (or the user is using an untested OS) anyway.  And when users do see it, having the info to be able to report to the driver developer is nothing but a *help*.  Maybe it’s confusing for some users, but I think easy access to useful debugging info is worth it.  And the confusion disappears as soon as the user is told that they need to provide the driver developer with the contents of the dialog (i.e. the error message).

    Of course, this is all my opinion; Microsoft obviously disagreed.  I guess I’m just a little annoyed (as a developer) that an easy method of getting some debug info was removed "because the user can’t do anything about it anyway".

  2. doronh says:

    Bryan, relying on OS symbols is a prerequisite for debugging any driver issue.  The fact that they are publicly available on a symbol server and you don’t have to search them out or copy them locally removes that issue almost entirely.

    As you said, a std user should never see this message.  If the user does, that means that the distribution of the driver is busted.

    I never said that this was the best method for debugging this issue either ;).  I was just showing how to use the OS itself to debug an issue if you are stuck.  User mode has much better tools.  For instance, you can use depends.exe to map out the import dependencies and flag any missing exports.  It has the added benefit of showing all missing imports vs one at a time with the method I demonstrated.

  3. strik says:

    doron: depends is a VS tool, isn’t it? I thought the DDK/WDK was a stand-alone product. Now, I need VS to debug an issue.

    Additionally, good look giving this a customer to find out what is missing on his machine, which is not missing on your own one.

    For example, I am used to have "customers" which are technically literate. They are used to give bug reports which are very helpful. In most cases, they can tell me what is wrong in much technical details.

    "Give a man a fish and he’ll eat it for the day. Teach him how to fish and he will eat for the rest of his life." This sentece makes sense, even for computers. Unfortunately, Miccrosoft seems to want to give only the fish to the people. Remember, there are even Windows users 😉 who want to learn fishinig, not only to have the fish, but Microsoft seems to be blind to this.

    Doron: No, this is NO rant against you, but more against some MS decisions. I am very happy you give us ways around these "limitations"!

    Anyway, I don’t think we are allowed to give depends.exe to some customer, are we?

    – Spiro.

  4. BryanK says:

    > I never said that this was the best method for debugging this issue either ;).

    I see what you’re saying; I was commenting on the part where you mentioned the reason behind removing the dialog in XP and beyond.  That likely wasn’t your decision to make, though, so I should probably go complain to someone else.  😉

    Anyway, thanks!  This blog is quite useful (especially the "debugger commands that make my life easier" series; I’ve used a few of them while trying to debug a BSOD in winpcap).

  5. doronh says:

    if a user ever sees this, the company/person who distributed the driver did not do due diligence in their quality assurance.  this type of error should only show up in house when the driver is in development and test.

    yes, you need VS for depends.exe.  You can get VS student edition for free if i remember correctly.  But it is a good point.  I will file a bug against the WDK to include depends.exe post Vista.

  6. doronh says:

    Updated the entry to reflect that on Windows 2000, the parameter which will contain the missing import is 5th, not 4th (thanks to Jake Oshins for pointing that out!).

  7. I found this one out the hard way today. I was experimenting with the KMDF loader driver (wdfldr.sys).