It’s fine to rename a function in your DEF file, but when you do, you have to link to that function by its new name


Jeffrey Riaboy asks why, if he renames a function in his DEF file, attempts to link to the function by its old name fail.

Well, um, yeah, because you renamed it.

Let's take the situation apart a bit; maybe it'll make more sense. I'm going to ignore a lot of details (dllimport/dllexport, calling conventions) since they are not relevant to the discussion and would end up just being distracting. I'm also going to assume we are running on an x86-class machine, just for concreteness. The same discussion works for other platforms; you just have to adjust the conventions accordingly.

First, here is some source code for a DLL, let's call it FRED.DLL:

int Dabba()
{
  return 0;
}

int Doo()
{
  return 1;
}

And here is the DEF file for FRED.DLL:

EXPORTS
 Yabba=Dabba
 Dabba=Doo

When you compile this DLL, the result will be something like this:

FRED.DLL:
 Yabba -> return 0;
 Dabba -> return 1;

The function exported as Yabba returns 0 because the DEF file said, "I want to export a function with the exported name Yabba; when somebody calls the function, I want control to go to the function I called Dabba internally."

Similarly, the function exported as Dabba returns 1 because the DEF file said, "I want to export a function with the exported name Dabba; when somebody calls the function, I want control to go to the function I called Doo internally."

Remember that symbolic information disappears during linking. The names of the functions and variables in the original source code are not stored anywhere in the DLL. The names exist only so that the linker can resolve symbolic references between object files. Once that's done, the names are discarded: Their work is done. (See The classical model for linking for a discussion of how linking works under the classical model.)

Exported functions are also a mapping between labels and functions, but this mapping is not used when linking the DLL; rather, it is just a table the linker produces under the direction of your DEF file. To reduce confusion for the programmer writing the DLL, the name in the exported function table usually matches the name in the object files, but that is merely a convention. An entry in the export table that doesn't perform renaming is just a shorthand for "I would like the exported name for this function to be the same as its internal name." It's a convenient typing-saver.

By analogy, Microsoft employees have one email address for use inside the company, and a different email address for use outside the company. Some employees choose to have their external email address be the same as their internal one, but that is hardly a requirement.

Meanwhile, the import library for our DLL looks something like this:

FRED.LIB:
 __imp__Yabba -> FRED.Yabba
 __imp__Dabba -> FRED.Dabba

 _Yabba@0 -> jmp [__imp__Yabba]
 _Dabba@0 -> jmp [__imp__Dabba]

As we saw before, each exported function results in two symbols in the import library, one with __imp_ prepended to the exported name, which represents the import table entry, and one containing a stub function for the benefit of a naïve compiler.

Now let's look at a program that wants to call some functions from FRED.DLL:

int Flintstone()
{
 Yabba();
 Dabba();
 Doo();
}

Let's say that these functions were not declared as dllimport, just for the sake of concreteness. (The discussion works the same if they were declared as dllimport, making the appropriate changes to the symbol names.) When the linker goes to resolve the call to Yabba@0, it will find the entry in FRED.DLL that says, "I've got a function called Yabba@0; the code for it is the single instruction jmp [__imp__Yabba]." When the program calls this function, the jmp instruction will jump through the import table entry for FRED.Yabba, which will wind up at the function in FRED.DLL exported under the name Yabba. If we look inside FRED.DLL, we see that this is a function that returns 0 (because it is the function which was called Dabba in the original source code, although that information was lost a long time ago).

Similarly, when the linker resolves the call to Dabba@0, it finds the entry in FRED.DLL which pulls in the one-line stub function which jumps through the import table entry for Dabba@0. This leads to a function that returns 1, a function which was called Doo in the original source code.

However, that last call to Doo raises a linker error because it cannot find a function called Doo in the FRED.LIB import library. That's just the internal name for a function in the source code for FRED.DLL, a name which was lost during linking. If you want to call the function which had been called Doo in the original source code, you have to import it by its new name, Dabba.

In Jeffrey's case, he took a function which was internally referred to by a decorated name (?Dispose@MyClass@@QAEAAV1@XZ) and renaming it to an undecorated name (MC_Dispose). But when other modules tried to use the library, they got the error saying that "?Dispose@MyClass@@QAEAAV1@XZ" is not found. Which is correct: ?Dispose@MyClass@@QAEAAV1@XZ was not found because it no longer exists under that name. You renamed it to MC_Dispose. Those modules need to link to the function MC_Dispose if they want to call the function "formerly known as ?Dispose@MyClass@@QAEAAV1@XZ".

Actually, Jeffrey's situation is more complicated than I described it because ?Dispose@MyClass@@QAEAAV1@XZ undecorates to public: class MyClass & __thiscall MyClass::Dispose(void); this is a method not a static function. I don't believe there's a way to override the name decoration algorithm for instance methods; the compiler is always going to generate a reference to ?Dispose@MyClass@@QAEAAV1@XZ. So renaming the export doesn't buy you anything because you don't control the name on the import side.

Comments (18)
  1. bahbar says:

    Hum… I’m somewhat confused at why Jeffrey tried to rename it at all, but even more confused by his claim that renaming it to "Dispose" would solve the link error. So… the compiler generates a reference to ?Dispose@MyClass@@QAEAAV1@XZ, the linker sees a reference to Dispose in the .lib, and successfully matches them ?

  2. GWO says:

    On a tangent, whats the usual method for exporting C++ class methods so that they can be dynamically loaded with , or called from VB?  Here we just wrap them with simple C functions, so

    int Foo::bar()

    gets wrapped with

    int bar(Foo*foo) {

      return foo->bar();

    }

    (Calling conventions elided, because I can never remember which I need ;) )

  3. Matthew says:

    @ bahbar –

    The reason to rename from a decorated name to an undecorated one during export is generally because the library (DLL) is expected to be used in explicit linking, where the decorated name can be unmanagable.

    Similarly, one might be building a DLL to be used as a plugin, whereby the available (exported) name would have to match the expectations of the plugin loader exactly. Generally, those interface functions will be undecorated (as again, dealing with decorated names here would be a considerable hassle, especially given that one could build plugins with any compiler, which means that decoration can vary).

  4. Jeffrey Riaboy says:

    Thanks for the response! I was not actually expecting a reply from this after so long. I forget you keep a year+ backlog :-).

    I looked back at the question I wrote and am a bit shocked at myself for not checking it over before submitting it. The way I wrote it didn’t fully explain my end intent, and it is a bit confusing.

    If I recall back to the situation, I had 2 classes in the DLL that both had an exported “Dispose” function. I did not want to keep the decorated names in the DLL file, so I used the export file to rename them to arbitrary names. I was hoping there was a way that other programs could still use the same header file to access the functions in the library. I know if the names were changed and the user only had the DLL, the linker would not know how to find the newly named functions. However, I was under the impression that if you gave the linker access to the .def or .lib file (or something else generated when the library was originally compiled with debugging/linking information) it would be able to find out what the function was renamed to, so it would know how to link.

    By undecorating the name only, and not renaming it, the function was still found during linking, as I guess the MSVC linker first checked for the decorated name, and when it was not found, for the undecorated name.

  5. bahbar says:

    @Matthew: I was not very clear. I know in general what renaming is useful for. But for member functions, I failed to see the point.

    Put it another way, did you ever find yourself wanting to rename a this-call ? How did you expect it to be called ?

  6. Jeffrey Riaboy says:

    I realized I didn’t really ask a question in my last post…

    Am I incorrect in assuming that the MSVC linker uses .lib files to gather extra information about linking to DLLs (or does it, but not renamed export functions)?

    If not, is there some type of file that can be included with a DLL (like a .lib or .def), or process that can be done in the header, that will automatically let the linker know what a function was renamed to?

    [Once the DLL is created, the original name of the function is lost. All that remains is the exported name. -Raymond]
  7. R Samuel Klatchko says:

    Reading this article made me wonder if you could export the same function twice with different names.  Continuing from Raymond’s example, could you do:

    EXPORTS

    Yabba=Dabba

    Dabba=Doo

    Doo=Doo

    The idea is this could simplify things when you both wanted to call a function with a mangled named both via implicit and explicit linking.

  8. Alexandre Grigoriev says:

    @R Samuel Klatchko,

    I wonder if redirect will work.

  9. Crescens2k says:

    @R Samuel Klatchko

    Who says you can’t export the same function more than once?

    A simple program which you could just write in under 5 minutes would give you the answer to that.

    For reference though, the following would work.

    LIBRARY dlltest

    EXPORTS

    baa

    oink=baa

    In your program which would reference it this, if you call oink or baa then it would call the same function.

  10. mikeb says:

    I just want to take a minute to say that this is a useful and very nicely written article about a topic – linking – that is generally under-documented (not just for Windows, but in general).  Thanks.

  11. Kemp says:

    Is it just me that sees this as being far too obvious? If you change the name… then you’ve changed the name. Of course linking against the old name would fail, because it doesn’t exist anymore. That’s like renaming a file and then wondering why typing the old filename doesn’t take you to it…

  12. Dan says:

    "Let’s take the situation apart a bit; maybe it’ll make more sense."

    Oh it made perfect sense just from the title, and still made sense from the opening paragraphs.

    Made sense right through the end, too.

  13. mikeb says:

    > Is it just me that sees this as being far too obvious? <<

    Like many things, it’s obvious once it’s been pointed out or explained. It might even be the most obvious way that things might work.  

    But note that it’s certainly not a given that a command in a .def file to provide the function export another name would necessarily result in the original name being dropped – there’s nothing that says a function (or address) can’t have 2 or more names.  I’m pretty sure the IAT in the binary wouldn’t have a problem with that.

    Even Raymond’s email analogy supports this – I know at least that I have more than one external email address.

    It’s been a long, long time since I messed around with .def files, but I wouldn’t be surprised if there’s even a supported way to do it today.

  14. mikeb says:

    > I wouldn’t be surprised if there’s even a supported way to do it today <<

    And now I notice that Crescens2k has already indicated that it’s quite simple to do so.

  15. James says:

    Instead of renaming decorated names in the .DEF file, it is more constructive and less work to declare exportable functions as extern "C".  

    In case of method calls – just don’t export them.  This is never a good idea since you will have to re-compile all the clients whenever you change the class private parts.  And compile all of them with the same compiler.

    Instead – always export a "C" API.

  16. Kemp says:

    But note that it’s certainly not a given that a command in a .def file to provide the function export another name would necessarily result in the original name being dropped

    Given that these are the only names available for the exports it still makes perfect sense to me.

    I should point out that I’ve never played with this in C before, only in a slightly different way when making DLLs in Delphi, but I don’t see a problem with it. Maybe it’s just one of those happy coincedences where what I expect lines up exactly with what happens :-)

  17. Neil says:

    Jeffery’s alternative is to make the function virtual in which case it doesn’t need to be exported, although for intra-module code this does result in an extra indirection.

  18. Matthew says:

    @ bahbar:

    Understood, and yes, it doesn’t make much sense to be renaming non-static member functions.

    @ James:

    Then you need to have a separate global API for any and all static member functions. This can mean additional maintenance tasks – for example, if the signature of an API function needs to change, you have to change it in two places instead of just one. (In the module providing the API, of course; still the same amount of work for any consumer.) Just something to consider when weighing the consequences of different methods.

    My personal approach does create a separate global API, but doesn’t bother to deal with extern "C". It uses a library of API-generating macros to define and export the wrapper functions. It more or less results in my own name decoration scheme, as the macros build unique names on the functions to be exported. Then usage macros (from the same master set) perform the same name-mangling to provide access by wrapping GetProcAddress(). The end result is a relatively clean means of access – the provider uses DECLARE_API() macros and the consumer uses CALL_API() macros with similar signatures (not quite identical; the declare needs types while the call needs actual variables, and the call also needs the module being used).

Comments are closed.

Skip to main content