Names in the import library are decorated for a reason

Date:July 27, 2006 / year-entry #252
Tags:other
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20060727-00/?p=30343
Comments:    23
Summary:When I wrote that the symbolic name for the imported function table entry for a function is called __imp__FunctionName, the statement was "true enough" for the discussion at hand, but the reality is messier, and the reason for the messy reality is function name decoration. When a naive compiler generates a reference to a function,...

When I wrote that the symbolic name for the imported function table entry for a function is called __imp__FunctionName, the statement was "true enough" for the discussion at hand, but the reality is messier, and the reason for the messy reality is function name decoration.

When a naive compiler generates a reference to a function, the reference is decorated in a manner consistent with its architecture, language, and calling convention. (Some time ago, I discussed some of the decorations you'll see on x86 systems.) For example, a naive call to the GetVersion function results in the compiler generating code equivalent to call _GetVersion@0 (on an x86 system; other architectures decorate differently). The import library therefore must have an entry for the symbol _GetVersion@0 in order for the external reference to be resolved.

To correspond to the stub function whose real name is _GetVersion@0 is the import table entry whose name is __imp__GetVersion@0. In general, the import table entry name is __imp_ prefixed to the decorated function name.

The fact that names in import libraries are decorated means that it is doubly crucial that you use the official import library for the DLL you wish to use rather than trying to manufacture one with an import library generation tool. As we noted earlier, the tool won't know whether the ordinal assigned to a named function was by design or merely coincidental. But what's more, the tool won't know what decorations to apply to the function (if the name was exported under an undecorated name). Consequently, your attempts to call the function will fail to link since the decorations will most likely not match up.

In that parenthetical, I mentioned exporting under undecorated names. Doesn't that mean that you can also export with a decorated name? Yes you can, but as I described earlier, you probably shouldn't. For as I noted there, if you export a decorated name, then that name cannot be located via GetProcAddress unless you also pass the decorated name to GetProcAddress. But the decoration schema changes from language to language, from architecture to architecture, and even from compiler vendor to compiler vendor, so even if you manage to pass a decorated name to the GetProcAddress function, you'll have to wrap it inside a huge number of #ifdefs so you pass the correct name for the x86 or ia64 or x64, accordingly, as well as changing the name depending on whether you're using the Microsoft C compiler, the Borland C compiler, the Watcom C compiler, or maybe you're using one of the C++ compilers. And woe unto you if you hope to call the function from Visual Basic or C# or some other language that provides interop facilities.

Just export those names undecorated. Your future customers will thank you.

(Exercise: Why is it okay for the C runtime DLLs to use decorated exports?)


Comments (23)
  1. Tom says:

    Because it most cases, the C Runtime (CRT) is specific to the compiler vendor.  

  2. BryanK says:

    Which is the same reason it was OK for the C runtime to use dllexport, in the “why can’t I GetProcAddress a function that I dllexported?” post, which was linked here under the “you probably shouldn’t” text.  ;-)

    [I often add exercises to keep the nitpickers quiet. -Raymond]
  3. Andy-Pennell says:

    The name decoration can also change between compiler versions from the same vendor: VC8 had changes compared to VC7.x for example.

  4. josh says:

    Well, for C it’s actually possible to safely link against code built with a different compiler.  You don’t have to worry about vtable layout, pointer to member representation, rtti, exceptions, etc., let alone how the other guy decorates his function to include the signature for overloading.  For C it’s just the 3 x86 calling conventions, which you basically need anyway to interact with Win32.

    For C++, forget it.  Exporting undecorated names only solves a part of the problem and will work some of the time but then crash when you least expect it.

    Of course for platforms with a sensible ABI this is simpler.

    [Not sure what the ABI has to do with this. If apps want to use some custom calling convention privately (“Hey, I’m going to implement function calls by passing the parameters in reverse alphabetical order of parameter name, putting the return address in the EAX register, and jumping to the start of the desired function!”) what’s to stop them from doing it? -Raymond]
  5. Ken Hirsch says:

    <i>Why is it okay for the C runtime DLLs to use decorated exports?</i>

    For _cdecl functions, the decorated names and undecorated names are the same.

  6. c++ says:

    How do I export fully undecorated names of c++ methods?

  7. John Andersson says:

    Is it as prone to errors to call a dll function with dynamically binding as with manufacturing an own import library? In both cases the original import library isn’t used.

  8. cringe says:

    Because trees look nicer with decorations.

  9. Anony Moose says:

    If you’re using LoadLibrary and GetProcAddress with decorated names, you’re in for a world of hurt when the next version of the library is compiled with a different release of the same compiler. If the names are undecorated (and you keep calling conventions, function signatures, etc the same), then you’re limited in certain C++ features you can use, but it will work fine.

    The problem with manufacturing an import library isn’t just the name – it’s the mucking around with ordinals that’ll screw you over.

    > How do I export fully undecorated names of c++ methods?

    With a great deal of pain and suffering.   ;)

    [Not sure what the ABI has to do with this. […] – Raymond]

    The fun with C++ is that a combination of different name mangling algorithms and no standard for the layout of vtables (etc), a class compiled with Visual C++ almost certainly can’t be used directly with code compiled with C++ Builder or GCC. With C, the ABI is specified reasonably well – mostly because it’s a lot simpler – so a DLL compiled with one compiler can generally be used with code compiled in many other compilers, provided this whole name decoration issue is handled correctly.

  10. theorbtwo says:

    c++: You don’t.  Insted, you go inside an extern "C" block, and write a function which calls your method, and call that from C.

    John Andersson: You’ve got to have an agreement between the parties doing dynamic linking that covers the things an import library would tell you, I think.

  11. c++ says:

    Why then is it possible at all to export c++ symbols? Can any compiler use them? Is it possible to tell any compiler to “look in this dll for the methods of this class”?

    Why is extern C better than def file?

    [Why is it possible to export C++ symbols? For the same reason it’s possible to export any other symbol. The import/export mechanism doesn’t care what you call your symbol, as long as the importer and exporter agree. You can create a PE file that exports a symbol called !?#! if you really want to. If you want to write a compiler/linker that supports those symbols then more power to you. Your next two questions are tools issues unrelated to the import/export mechanism. And I don’t understand the last question. “Extern C” and DEF files are independent concepts. -Raymond]
  12. Norman Diamond says:

    Thursday, July 27, 2006 1:02 PM by josh

    > Well, for C it’s actually possible to safely

    > link against code built with a different

    > compiler.

    Only if one of the compiler developers copied the calling convention(s) from the other (or if the two compiler developers cooperated with each other).

    > For C it’s just the 3 x86 calling

    > conventions, which you basically need anyway

    > to interact with Win32.

    Mr. Chen already pointed out that applications can define additional calling conventions, and the fact is that compiler developers can too.  If compiler developers target Win32 then they have to include Win32 calling conventions among the variations that they support.

    The first time I saw "Pascal" as a calling convention in Windows I just figured, great, Windows calling conventions fluctuate according to which Pascal compiler you use this month.  Some time later I discovered that Microsoft started calling this __stdcall instead, with the implication that Microsoft actually knew what calling convention they were using and there was some way for callers to find out what to conform to.

  13. josh says:

    Ok, if you have to deal with malicious or insane compiler writers, you can’t safely link any code built with different compilers.  They could implement integers with some sort of balanced ternary, and then you’d be totally screwed.

  14. Miral says:

    So, to try to sum up a bit… Raymond, you’re basically recommending that everyone should always use __declspec(dllimport), and should use __declspec(dllexport) seldom (only on those routines that are compiler-specific anyway, or on extern "C" cdecl routines), replacing __declspec(dllexport) with .def files in most cases?  Is that correct?

  15. Chris Becke says:

    Actually Miral, I didn’t see that recommendation at all.

  16. Dean Harding says:

    Why then is it possible at all to export c++ symbols?

    You can export C++ symbols because sometimes you want to.

    For example, if the DLL is actually shipped with your application and isn’t actually SHARED, then there’s no chance that some other compiler will try to link to it.

    Why would you put your code in a separate DLL in that case? Well, perhaps you ship multiple executables that have to link to the same code. If you update everything in one go, you can just export your C++ symbols directly, and life if rather easy.

  17. Ulric says:

    Crikey, this is all newbie stuff, and it hasn’t changed in the last 10 years.

    If you make a DLL to be used by other products, yes, you export the symbol as extern "C" and you stay away from C++.  Then if the API is properly designed, your DLL can work with all compilers for years to come!

    Unless you make a third-party C++ library (ex: MFC, QT) in which case you dllexport the classes, and you will need to keep up with the compiler updates and have different versions of your DLL for different compiler vendors and versions.

    If you write .def files instead of using the non-standard keyword __dllexport(), really you need to use the ‘extern "C" because otherwise guessing the decorated name you need to write in the .def file is a huge waste of your time, and it may break in future.  In C++, no human should be writing .def files, so that means forget about .def file features like assigning ordinal numbers.   You might need to make debug and release versions as well, because c++ could inline a method.

    If you make a DLL for your own product’s internal use, none of this matters and you’d  just dllexport everything.  You’re always compiling/shipping a new version of your DLL with the App anyway. When you use extern "C" on function, you won’t be able to overload them, which can be a pain or a feature.

    About ‘pascal’.  Pascal was in fashion back in Windows 16-bit and that was the only agreed-upon calling convension.  The MacOS API is also all pascal.  Pascal was the big thing, not C.

    When moving to win32, MS changed to ‘stdcall’ as the agreed-upon calling convention (plus they had the leading compiler at this point).  You’ll only see ‘pascal’ in header file that date from the 16-bit days, __pascal has never been implemented, afaik, in a 32-bit C compiler.

    Why is it OK for the C Run Time library to use decorated name. That’s obvious!  The C Run Time Library is ALREADY compiler-dependant, and therefore doesn’t need to use compiler-independant undecorated names.  A new C Run time always come with a new compiler version.

  18. Csaba says:

    About ‘pascal’.  Pascal was in fashion back in Windows 16-bit

    The pascal calling convention in 16-bit Windows was chosen because of efficiencey, not fashion. The callee cleans up the stack, which means that the stack pointer needs to be adjusted with the size of the arguments. For a function with fixed arguments, this can be computed at compile-time. The Intel processors have hardware support for this ("RET n" instruction).

  19. Mike says:

    Actually, there are valid and good uses for both exported C++ (member) functions and exporting them only by ordinal.

    I inherited a C++ library (compiler-specific, of course) that exported quite a lot of C++ member functions. In fact, it previously had used dllexport on the whole classes. I’m not exaggerating when I say that just the names accouted for over 30% of the DLL.

    Applying a bit of carefully designed ordinal ranges, and using the .def file only exporting the symbols by ordinal, this DLL became even lean, and loaded quite a bit quicker.

    All it takes is careful ordinal-range reservations, and knowing "This is from now on carved in stone". Actually, as this was C++ it’d have to be considered carved in stone anyway, as one library+dll only fits a specific version from a specific vendor. It’s a bith that one needs to ship 10+ DLL’s, but that’s the way it is without a common C++ ABI.

  20. Norman Diamond says:

    Friday, July 28, 2006 4:47 PM by Csaba
    > The pascal calling convention in 16-bit
    > Windows was chosen because of efficiencey,
    > not fashion. The callee cleans up the stack

    And Ulric said “that was the only agreed-upon calling convension”.  That was still miraculous…

    > The Intel processors have hardware support
    > for this (“RET n” instruction).

    And the VAX had an instruction for the caller to get the number of arguments.  Other architectures pretty much did not agree in this manner.  Pascal was implemented on a lot of architectures (it was in fashion for a while, with good reason).

    Some architectures had several implementations made by various parties.  In some cases the hardware vendor was also the OS vendor so they specified the calling conventions and there was no question about agreements on those architectures.

    The PC didn’t fit that case.  If all implementors of Pascal for the PC agreed on a single calling convention, that was miraculous.

    [You’re ascribing far too much importance to the meaning of the word “pascal”. Perhaps it should have been called the “FRIBBY” calling convention. Then people wouldn’t be so confused. -Raymond]
  21. Michiel Salters says:

    The real question is, why don’t we have a GetProcAddress yet which understands name mangling? If I want the C++ function named

    int foo(int,int), I just want to call GetProcAddress<int(*)(int,int)>("foo") and have it mangle "foo". Of course, that GetProcAddress would need to be a compiler intrinsic.

    It’s just two steps:

    1. Mangle the argument (any compiler can)

    2. Call real GetProcAddress() (any compiler can)

  22. Andrew Barnert says:

    Michiel: Why? Wouldn’t you rather have a directly-accessible function for mangling (and demangling), rather than burying it inside a GetProcAddress wrapper? (Writing your own wrapper based on the mangler would be trivial; vice-versa is obviously not.)

    Also, I’m having a hard time envisioning what you want to use this for that wouldn’t

    a. involve building/shipping the DLL and caller together, in which case do you really need GetProcAddress;

    b. be better served by giving your overloads different names, probably through extern "C" wrappers, in the first place; or

    c. require mangling at runtime.

  23. I found this list of article on Raymond&#39;s blog . Raymond&#39;s blog is one of the more interesting

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