How a less naive compiler calls an imported function

Date:July 24, 2006 / year-entry #246
Tags:other
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20060724-00/?p=30403
Comments:    9
Summary:If a function is declared with the dllimport declaration specifier, this instructs the Visual Studio C/C++ compiler that the function in question is an imported function rather than a normal function with external linkage. With this additional information, the compiler generates slightly different code when it needs to reference an imported function, since the compiler...

If a function is declared with the dllimport declaration specifier, this instructs the Visual Studio C/C++ compiler that the function in question is an imported function rather than a normal function with external linkage. With this additional information, the compiler generates slightly different code when it needs to reference an imported function, since the compiler is aware of the special way imported functions are implemented.

First, there is no need for the stub function any more, because the compiler can generate the special call [__imp__FunctionName] instruction inline. Furthermore, the compiler knows that the address of an imported function never changes, and consequently it can optimize away multiple loads of the function pointer:

    mov   ebx, [__imp__FunctionName]
    push  1
    call  ebx ; FunctionName(1)
    push  2
    call  ebx ; FunctionName(2)

(Note to crazy people: This optimization means that you can run into problems if you patch a module's import table once it has started running, because the function pointer may have been optimized into a register before you patched the import. Consider, in the above example. that you patched the __imp__FunctionName table entry after the mov ebx, [__imp__FunctionName] instruction: Your replacement import table entry won't be called since the old function address was cached in the ebx register.)

Similarly, if your program tries to take the address of an imported function that has been declared with the dllimport declaration specifier, the compiler recognizes this and converts it to a load from the imported function address table.

As a result of this extra knowledge imparted to the compiler, the stub function is no longer needed; the compiler knows to go straight to the imported function address table.

Note that there are still occasional circumstances wherein you can induce the stub function to be created. We'll take a look at them (and related dangers) next time.


Comments (9)
  1. Grant says:

    Is there a reason these ‘imp‘ functions don’t show up in ‘dumpbin /exports’, or am I just looking at the wrong dlls?

    I ask because I’m generating some .coff files myself, and functions are linking fine but pointers to structs aren’t resolving properly.  I thought dumpbin.exe output would be definitive, and everything looks good there, but it looks like I might need to look at the raw data.

  2. Chris Becke says:

    @Grant: the imp function dont show up in the dumpbin, because they dont exist in the Dll. They exist only in the .lib

  3. Antonio Vargas says:

    Hmmm.. so it turns "a = &b;" into "a = *_imp_b;"? I can be pretty sure people have lost a lot of hair trying to debug his programs and realizing the compiler was changing the code behind their backs… nice trick anyhow ^^

  4. steveg says:

    Antonio: I’d expect the register optimisation Raymond describes is only done for Release builds, not Debug.

  5. Moff says:

    Uh – oh. Finally it makes sense what Jeffery Richter wrote in his book ("Advanced Windows Programming"), when elaborating about thread injection and getting an address of the function stub. Thank you, Raymond.

  6. peterchen says:

    >Note to crazy people

    I like that term :)

    Curious: What are performance implications? Why do PSDK headers not
    use this (for supported compilers) – to much work for to little
    benefit, or something else?

    [The performance implications of what? The imp mechanism? THe SKD headers do use this – see the first sentence of this posting. -Raymond]
  7. When you ask C to do things it can’t.

  8. moo says:

    The patching would still be safe if it was done from inside the function at the original address and was idempotent.

    (In other words: if calling the old function through the cached address in the register simply resulted in it being patched again and then the new function being called)

  9. peterchen says:

    Ooops – checked again, and it was there in all it’s glory:

    #if !defined(USER32)

    #define WINUSERAPI DECLSPEC_IMPORT

    #else

    #define WINUSERAPI

    #endif

    Last time I checked I noticed onl the empty definition branch. There aredefinitely advantages to those who can read :-)

    Seems like the typical define you make for building vs. consuming a DLL.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index