Weird side affect of the day: #defines can be used in your .def file


I found this one out the hard way today. I was experimenting with the KMDF loader driver (wdfldr.sys). I added the following #define to my sources file so that I could share code between wdfldr and another component and control some functionality based on who was including the file

sources:
TARGETNAME=wdfldr
TARGETTYPE=EXPORT_DRIVER

C_DEFINES=$(C_DEFINES) -DWDFLDR=1

[…]

I then compiled my test driver (wdfrawbusenumtest.sys), but it failed to load! I was getting a code 39 (which meant Windows could not load my driver). I previously wrote on how to debug a missing export at runtime, and I tried to use that technique. It told me that “KeGetCurrentIrql” was unresolved, but that is not possible since hal.dll has exported this function since NT 3.1. It finally occurred to me look at the imports for my test driver to see if there was anything weird. Here is what I saw:
    link /dump /imports WdfRawBusEnumTest.sys

[…]
HAL.dll
18014 Import Address Table
1C290 Import Name Table
0 time date stamp
0 Index of first forwarder reference

4C KeGetCurrentIrql

1.SYS
18000 Import Address Table
1C27C Import Name Table
0 time date stamp
0 Index of first forwarder reference

7 WdfVersionBind
9 WdfVersionUnbind

Huh? 1.sys? Where did that come from? I went back and checked the src file (which when compiled becomes the def file) and there were no changes there. This is what the def file looks like:
wdfldr.src:
NAME WDFLDR.SYS

EXPORTS
WdfVersionBind
WdfVersionUnbind
[…]

Since I didn’t change the def file, what was making the compiler think that the exports should resolve to 1.sys? I finally realized that my C_DEFINE of -DWDFDLR=1 was the culprit. When wdfldr.src was being built into wdfldr.def, my define changed NAME WDFLDR.SYS into NAME 1.SYS. Little did I know that the C preprocessor was used when compiling the src file. To fix the problem, all I needed to do was change the define to something else, C_DEFINES=$(C_DEFINES) -D_WDFLDR_=1, and then update my shared headers to use the new define.

Comments (4)

  1. RichardRudek says:

    In the previous article you linked to, some people were complaining about the distribution of DEPENDS.EXE.

    I did a search of your blog, and it seems you’ve not talked about Dependency Walker. So I thought I should point it out to you and your readers. It’s free !

    http://www.dependencywalker.com/

  2. doronh says:

    That looks like a great tool, I am going to check it out when I have a free moment.

    d

  3. aionescu says:

    Doron, I think the whole point of the .src file is that it’s pre-processed, otherwise you can simply use a run of the mill .def. From the documentation:

    <<<

    Additionally, there is a standard inference rule that will run the C preprocessor over the .def or .src file in the current directory to create build-specific .def files in the object subdirectory. The same compiler define directives that you key off of in your source code can be used in the root .def or .src file to create the export list. To enable it, use something like:

    DLLDEF=$Omydll.def

    Here is an example of what mydll.src might look like:

    ; mydll.src

    LIBRARY mydll

    EXPORTS

    #if defined(_X86_)

    X86Routine

    #elseif defined(_IA64_)

    IA64Routine

    #else

    #error Unknown platform

    #endif

    // Grab some other exports from another file

    #include "master.src"

    >>>

    Best regards,

    Alex Ionescu

  4. doronh says:

    yes, it is obvious after the fact and once I remembered the contents of other *.src files, but at the time, it was a bit perplexing and the way that the preprocessor changed things made it completely non obvious what was going on ;).

    d